6

これを適切に同期する方法は?現時点では、完了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);
   }
}

最善の解決策は、truedataAlreadyBeenSetAndWaitingToBeProcessedに設定された新しいブール変数を導入SetDataし、最後にDoLongOperationWith_dtrue に設定できるようにするSetDataことでしょうか。この変数を true に設定して が呼び出された場合、単に戻ることができますか?

4

3 に答える 3

5

これはテストされていませんが、.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; 
             }
     }
}
于 2009-08-17T23:32:03.527 に答える
2

ここには、問題になる可能性のあるシナリオが 2 つあります。

1:

  • DoLongOperationWith_d(d) が終了します。
  • SetData() が呼び出され、d に新しい値が格納されます。
  • e.WaitOne() が呼び出されますが、値が既に設定されているため、スレッドは永久に待機します。

そこが気になるなら、安心していいと思います。ドキュメントから、

AutoResetEvent がシグナル状態のときにスレッドが WaitOne を呼び出した場合、スレッドはブロックされません。AutoResetEvent はスレッドをすぐに解放し、非シグナル状態に戻ります。

それは問題ではありません。ただし、SetData() がいつ、どのように呼び出されるかによっては、より深刻な問題に対処している可能性があります。

2:

  • SetData() が呼び出され、新しい値が d に格納され、ランナーが起動されます。
  • DoLongOperationWith_d(d) が開始されます。
  • SetData() が再度呼び出され、d に新しい値が格納されます。
  • SetData() が再び呼び出されます。d の古い値は永久に失われます。DoLongOperationWith_d() が呼び出されることはありません。

それが問題である場合、それを解決する最も簡単な方法は同時キューです。実装は豊富です。

于 2009-08-17T23:32:02.827 に答える
1

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

このアプローチでできることの 1 つは、SetData にタイムアウトを取得させ、それを に渡すことWaitOneです。ただし、調査する必要があると思いますThreadPool.QueueUserWorkItem

于 2009-08-17T23:55:27.147 に答える