你能用C#线程同步进行原子信号和等待吗?

hoo*_*law 4 c# synchronization thread-safety

我在C#中遇到了一些线程同步问题.我有一个共享对象,它被两个线程操纵,我使用lock()访问了互斥的对象,但我也希望根据共享对象的状态阻塞每个线程.当对象为空时特别阻塞线程A,当对象满时阻塞线程B,当对象状态改变时让另一个线程发出阻塞线程信号.

我尝试使用ManualResetEvent执行此操作,但遇到竞争条件,其中线程B将检测对象已满,移至WaitOne,并且线程A将进入并清空对象(每次访问都发出MRE信号,并阻止自身一次在线程A命中WaitOne之前,对象是空的,这意味着线程A正在等待线程未满,即使它不是.

我想如果我可以调用像'SignalAndWaitOne'这样的函数,那么在等待之前它会原子地发出信号,它会阻止这种竞争条件吗?

谢谢!

Eri*_*ert 7

执行此操作的典型方法是使用Monitor.Enter,Monitor.Wait和Monitor.Pulse来控制对共享队列的访问.草图:

shared object sync = new object()
shared Queue q = new Queue()

Producer()
    Enter(sync)
    // This blocks until the lock is acquired
    while(true)
        while(q.IsFull)
            Wait(sync)     
            // this releases the lock and blocks the thread 
            // until the lock is acquired again

        // We have the lock and the queue is not full.
        q.Enqueue(something)
        Pulse(sync)
        // This puts the waiting consumer thread to the head of the list of 
        // threads to be woken up when this thread releases the lock

Consumer()
    Enter(sync)
    // This blocks until the lock is acquired
    while(true)
        while(q.IsEmpty)
            Wait(sync)
            // this releases the lock and blocks the thread 
            // until the lock is acquired again

        // We have the lock and the queue is not empty.
        q.Dequeue()
        Pulse(sync)
        // This puts the waiting producer thread to the head of the list of 
        // threads to be woken up when this thread releases the lock
Run Code Online (Sandbox Code Playgroud)


Ste*_*ary 5

A BlockingCollection已由.NET 4.0提供.

如果您使用的是早期版本,则可以Monitor直接使用该类.

编辑:以下代码完全未经测试,并且不处理maxCount小(<= 2)的值.它也没有任何超时或取消的规定:

public sealed class BlockingList<T>
{
  private readonly List<T> data;
  private readonly int maxCount;

  public BlockingList(int maxCount)
  {
    this.data = new List<T>();
    this.maxCount = maxCount;
  }

  public void Add(T item)
  {
    lock (data)
    {
      // Wait until the collection is not full.
      while (data.Count == maxCount)
        Monitor.Wait(data);

      // Add our item.
      data.Add(item);

      // If the collection is no longer empty, signal waiting threads.
      if (data.Count == 1)
        Monitor.PulseAll(data);
    }
  }

  public T Remove()
  {
    lock (data)
    {
      // Wait until the collection is not empty.
      while (data.Count == 0)
        Monitor.Wait(data);

      // Remove our item.
      T ret = data.RemoveAt(data.Count - 1);

      // If the collection is no longer full, signal waiting threads.
      if (data.Count == maxCount - 1)
        Monitor.PulseAll(data);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)