你能用C#线程同步来发出信号并等待吗

本文关键字:信号 等待 线程 同步 | 更新日期: 2023-09-27 17:57:29

我在C#中的线程同步方面遇到了一些问题。我有一个由两个线程操作的共享对象,我已经使用lock()对该对象进行了互斥访问,但我也想根据共享对象的状态来阻止每个线程。特别是当对象为空时阻塞线程A,当对象为满时阻塞线程B,当对象状态发生变化时,让另一个线程向被阻塞的线程发出信号。

我试着用ManualResetEvent来做这件事,但遇到了一个竞争条件,线程B将检测到对象已满,移动到WaitOne,线程a将在线程a到达其WaitOne之前进入并清空对象(每次访问都向MRE发出信号,一旦对象为空,就会阻塞它自己),这意味着线程a正在等待线程未满,即使它没有满。

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

谢谢!

你能用C#线程同步来发出信号并等待吗

一种典型的方法是使用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

.NET 4.0已经提供了BlockingCollection

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

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

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);
    }
  }
}