0

因此,我正在阅读有关同步的内容并遇到过Monitors,但似乎无法掌握它们的工作原理。

我看到一般布局是以下格式的东西,我可以看到条件变量(我不确定这些是做什么的):

monitor {
    // shared variables
    procedure P1 (..) {..}
    ...
    procedure P2 (..) {..}
    initialisation code (..) {..}
}

但是,尽管我对此一头雾水,但我看不出真正看到像所描述的那样的监视器如何一次只允许一个进程访问。

4

1 回答 1

1

我喜欢将监视器视为具有私有互斥锁的对象,并且每个过程都受到该互斥锁的保护。所以像:

MyClass {
  // private shared variables
  ...
  mutex m;
  // public procedures
  Procedure P1(...) {
    acquire(m);
    ...
    release(m);
  }
  Procedure P2(...) {
    acquire(m);
    ...
    release(m);
  }
  initialisation code (...) { ... }
}

互斥锁保证一次只有一个线程/进程可以操作对象,因为每个公共过程都包含互斥锁获取/释放。

现在那些条件变量呢?嗯,通常你不需要它们。但有时你有一些条件,一个线程需要等待另一个线程,这就是条件变量的来源。

例如,假设您正在实现一个并发队列对象。你会有一个push()程序和一个pop()程序。但是如果队列是空的怎么办?

由于代码如下:

Thread A                Thread B
if (not q.empty())
                        if (not q.empty())
                        then q.pop() // now q is empty!
then q.pop() // error!

因此,您必须定义一个 pop() 过程,该过程在队列为空时返回一个特殊值,然后旋转等待队列变为非空。但是旋转等待是非常低效的。

因此,您改为使用条件变量。条件变量有两种方法:wait()notify()。Wait() 原子地放弃你持有的互斥锁,然后阻塞线程,直到其他线程调用 notify()。所以像这样:

condition_variable cv;
Procedure int pop() {
  acquire(m);
  while (q.empty()) {
    wait(cv, m)  // atomically release m and wait for a notify() on cv.
    // when wait() returns it automatically reacquires mutex m for us.
  }
  rval = q.pop();
  release(m);
  return rval;
}
Procedure push(int x) {
  acquire(m);
  q.push(x);
  notify(cv);  // wake up everyone waiting on cv
  release(m);
}

现在您有了一个数据结构/对象,其中一次只能有一个线程访问该对象,并且您可以处理不同线程之间的操作需要以特定顺序发生的情况(例如至少一个 push()必须在每个 pop() 完成之前发生。)

于 2013-04-10T22:54:01.383 回答