如何正确进行同步这个吗?目前,它是可能的, SetData 是后调 e.WaitOne() 已完成了所以 d 可能是已经设置到另一个价值。我试着插入的锁,但它导致了进一僵局。

AutoResetEvent e = new AutoResetEvent(false);

public SetData(MyData d)
{
   this.d=d;
   e.Set();    // notify that new data is available
}

// This runs in separate thread and waits for d to be set to a new value
void Runner() 
{    
   while (true)
   {
      e.WaitOne();  // waits for new data to process
      DoLongOperationWith_d(d);
   }
}

将最好的解决方案是要引入一个新布尔变量 dataAlreadyBeenSetAndWaitingToBeProcessed 这是设置在 SetData 要真正结束时 DoLongOperationWith_d 它可能被设置为真实,所以如果 SetData 是叫这个变量设定为真的这可能只是回来吗?

有帮助吗?

解决方案

这是未经测试,但是一种优雅的方式与基于.NET原语来做到这一点:

class Processor<T> {
    Action<T> action;
    Queue<T> queue = new Queue<T>();

    public Processor(Action<T> action) {
        this.action = action;
        new Thread(new ThreadStart(ThreadProc)).Start();
    }

    public void Queue(T data) {
        lock (queue) {
            queue.Enqueue(data);
            Monitor.Pulse(queue); 
        }            
    }

    void ThreadProc() {
        Monitor.Enter(queue);
        Queue<T> copy;

        while (true) {                 
            if (queue.Count == 0) {
                Monitor.Wait(queue);
            }

            copy = new Queue<T>(queue);
            queue.Clear();
            Monitor.Exit(queue);

            foreach (var item in copy) {
                action(item); 
            }

            Monitor.Enter(queue); 
        }
    }
}

实施例的程序:

class Program {

    static void Main(string[] args) {

        Processor<int> p = new Processor<int>((data) => { Console.WriteLine(data);  });
        p.Queue(1);
        p.Queue(2); 

        Console.Read();

        p.Queue(3);
    }
}

这是一个非队列版本,队列版本可能是优选的:

object sync = new object(); 
AutoResetEvent e = new AutoResetEvent(false);
bool pending = false; 

public SetData(MyData d)
{
   lock(sync) 
   {
      if (pending) throw(new CanNotSetDataException()); 

      this.d=d;
      pending = true;
   }

   e.Set();    // notify that new data is available
}

void Runner() // this runs in separate thread and waits for d to be set to a new value
{

     while (true)
     {

             e.WaitOne();  // waits for new data to process
             DoLongOperationWith_d(d);
             lock(sync) 
             {
                pending = false; 
             }
     }
}

其他提示

有两个可能令人不安的情况在这里。

1:

  • DoLongOperationWith_d(d)完成。
  • 或者设置data()称,存储一个新的价值在d。
  • e.使用此()称,但由于价值已经设置的线等待直到永远。

如果那是你的关心,我想你可以放松。从 文档, 我们看到,

如果有线电话使用此同时AutoResetEvent是在终止状态,线不块。该AutoResetEvent释放线,并立即返回到非发出信号的状态。

所以那不是个问题。然而,这取决于如何和何时或者设置data()是所谓的,你可以处理更严重的

2:

  • 或者设置data()称,存储一个新的价值在d和唤醒亚军。
  • DoLongOperationWith_d(d)开始。
  • 或者设置data()再次调、储存新的价值在d。
  • 或者设置data()称为了!老值d失去的永远;DoLongOperationWith_d()将永远不得援引。

如果这是你的问题,最简单的方法来解决这是一个并发的队列。实现中比比皆是。

可以使用2个事件,

AutoResetEvent e = new AutoResetEvent(false);
AutoResetEvent readyForMore = new AutoResetEvent(true); // Initially signaled

public SetData(MyData d)
{
   // This will immediately determine if readyForMore is set or not.
   if( readyForMore.WaitOne(0,true) ) {
     this.d=d;
     e.Set();    // notify that new data is available
  }
  // you could return a bool or something to indicate it bailed.
}

void Runner() // this runs in separate thread and waits for d to be set to a new value
{

     while (true)
     {

             e.WaitOne();  // waits for new data to process
             DoLongOperationWith_d(d);
             readyForMore.Set();
     }
}

一个你可以用这种方法做的事情是具有的SetData采取超时,并传递到WaitOne。我想,无论你shoudl调查ThreadPool.QueueUserWorkItem

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top