2

私は2つのクラスを持っています:

public class A
{
    private const int MAXCOUNTER = 100500;
    private Thread m_thrd;
    public event Action<string> ItemStarted;
    public event Action<string> ItemFinished;

    private void OnItemStarted(string name)
    {
        if (ItemStarted != null) ItemStarted(name);
    }

    private void OnItemFinished(string name)
    {
        if (ItemFinished != null) ItemFinished(name);
    }

    public A()
    {
        m_thrd = new Thread(this.Run);
        m_thrd.Start();
    }

    private void Run()
    {
        for (int i = 0; i < MAXCOUNTER; i++)
        {
            OnItemStarted(i.ToString());
            // some long term operations
            OnItemFinished(i.ToString());
        }
    }
}

public class B
{
    private Thread m_thrd;
    private Queue<string> m_data;
    public B()
    {
        m_thrd = new Thread(this.ProcessData);
        m_thrd.Start();
    }

    public void ItemStartedHandler(string str)
    {
        m_data.Enqueue(str);
    }

    public void ItemFinishedHandler(string str)
    {
        if (m_data.Dequeue() != str)
            throw new Exception("dequeued element is not the same as finish one!");
    }

    private void ProcessData()
    {
        lock (m_data)
        {
            while (m_data.Count != 0)
            {
                var item = m_data.Peek();
                //make some long term operations on the item
            }
        }
    }
}

また、コードには別の場所があります

A a = new A();
B b = new B();
a.ItemStarted += b.ItemStartedHandler;
a.ItemFinished += b.ItemFinishedHandler;
  • では、がまだ動作しているときに がItemFinished発生した場合ProcessData()、どうなるでしょうか?
  • クラスの終了時にクラスを待機さAutoResetEventせるようなものを使用する必要がありますか?ABProcessData
  • lockで使用する必要がありますProcessDataか?
  • でクラスのスレッドを呼び出しBてもm_thrd = new Thread(this.ProcessData);よろしいですか? ここで私を混乱させています-イベントが発生ProcessDataする前に終了しません(最初に生成されたときにスレッドがすでに終了してItemStartedいるという状況につながりませんか)?BItemStarted
4

2 に答える 2

1

あなたのコードは少しスレッドが混乱しています。

が終了ItemFinishedする前に発生した場合、最初の問題は競合状態です。ProcessData()

の使用について心配する必要はありませんAutoResetEvent。簡単なことは、 へのすべてのアクセスをロックすることm_dataです。それは次の質問につながります - はい、ロックは必要であり、どこでも必要です。

しかし、あなたの最後のポイントが最も重要です。各コンストラクターを.Start()メソッドに変更して、開始する前に接続する時間を確保する必要があります。

しかし、それでも大規模な競合状態があります。

これを機能させるには、次のことを行う必要があります。NuGet "Rx-Main" を使用して、Microsoft の Reactive Framework をコードに追加します。次に、これを行います:

var scheduler1 = new EventLoopScheduler();
var scheduler2 = new EventLoopScheduler();

Observable
    .Range(0, 100500)
    .ObserveOn(scheduler1)
    .Do(x => { /* some long term operations */ })
    .ObserveOn(scheduler2)
    .Subscribe(x =>
    {
        //make some long term operations on the item
    });

ジョブ完了。

于 2015-08-15T08:44:27.723 に答える
0

コード内のいくつかのスレッドの問題を修正する必要があります。

  1. スレッドの開始後にイベントを配線しています。スレッドを開始する前にこれを行う必要があります。したがって、両方のクラスについて、コンストラクターにあるものは何でも Public インスタンス関数に入れます。クラスのインスタンスを作成し、イベントを接続してから、最初にクラス B のインスタンス メソッドを呼び出し、次にクラス A を呼び出してスレッドを開始します。
  2. 2 つの異なるスレッドからクラス B の Queue にアクセスしています。キューはスレッド セーフではないため、代わりに ConcurrentQueue ( https://msdn.microsoft.com/en-us/library/dd267265(v=vs.110).aspx ) と対応する Try... 関数を使用する必要があります。ConcurrentQueue では、いかなる種類の明示的なロックも必要ありません。
  3. クラス B の ProcessData 関数を使用する意図はあまり明確ではありません。キュー内のすべてのアイテムに対して ProcessData を 1 回呼び出したいですか、それともアイテムがデキューされなくなるまで ProcessData を呼び出し続けたいですか? 前者の場合、Queue を使用する代わりに、GetConsumingEnumerable で BlockingCollection を使用する必要があります。詳細については、https ://msdn.microsoft.com/en-us/library/dd287186(v=vs.110).aspx を参照してください。これがあなたが望むものなら、私はサンプルを投稿できます。BlockingCollection を使用すると、ProcessData がクラス A スレッドよりも早く終了することはありません。
于 2015-08-17T02:19:10.483 に答える