C#マルチスレッド同期イベントおよび待機ハンドル


最近マルチスレッドの同期問題をいじってみると、実はC#はマルチスレッドの同期イベントの処理についてまだ柔軟であることが分かった.ここでは主に、自分でテストしたコードの一部を書いて、AutoResetEventとManualResetEventに関連している.もちろん、Systemも簡単に述べた.Threading.WaitHandle.WaitOne 、System.Threading.WaitHandle.デイジーとシステムThreading.WaitHandle.WaitAll,次は最初の学者の観点から,マルチスレッド間の同期を見る.
このようなシーンがあると仮定すると、メインスレッドはサブスレッドを開き、サブスレッドを待たせ、メインスレッドが何かを完了したときにサブスレッドに下へ実行するように通知します.ここで重要なのは、サブスレッドをどのように待たせるか、メインスレッドがサブスレッドにどのように通知するかです.一般的には、共通変数を使用することは難しくありません.そこで、次のコードがあります.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace AutoResetEventTest
{
    class Class1
    {
        static bool flag = true;

        static void DoWork()
        {
            Console.WriteLine("   worker thread started, now waiting on event...");
            while (flag)
            {

            }
            Console.WriteLine("   worker thread reactivated, now exiting...");
        }

        static void Main()
        {
            Console.WriteLine("main thread starting worker thread...");
            Thread t = new Thread(DoWork);
            t.Start();

            Console.WriteLine("main thrad sleeping for 1 second...");
            Thread.Sleep(1000);

            Console.WriteLine("main thread signaling worker thread...");
            flag = false;
        }
    }
}

目的は達成したが、このコードを見ていると葛藤し、次は私たちの主役が登場するはずだ.AutoResetEventとManualResetEvent、この2つについてはしばらく差がないと思っているが、後で彼らの違いを紹介する.ここではAutoResetEventを例に挙げると、多くの公式の言い方が抽象的すぎる.ここでは通俗的に言えば、AutoResetEventはイベントであるにもかかわらず共通の変数であり、作成時にfalseに設定し、待機するスレッドでWaitOneメソッドを使用すると、スレッドは常に待機状態になり、このAutoResetEventだけが別のスレッドでSetメソッドを使用し、つまり通知するスレッドがSetメソッドを使用すると、待機するスレッドは下へ実行されると考えられます.Setは信号で、WaitOneは待機信号で、信号を送っただけで、待機しているだけが実行されます.もし出さなければ、WaitOneの後のプログラムは永遠に実行されません.では、AutoResetEventで上記のプログラムを改造します.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace AutoResetEventTest
{
    class Class2
    {
        static AutoResetEvent mEvent=new AutoResetEvent(false);
        //static ManualResetEvent mEvent = new ManualResetEvent(false);

        static void DoWork()
        {
            Console.WriteLine("   worker thread started, now waiting on event...");
            mEvent.WaitOne();
            Console.WriteLine("   worker thread reactivated, now exiting...");
        }

        static void Main()
        {
            Console.WriteLine("main thread starting worker thread...");
            Thread t = new Thread(DoWork);
            t.Start();

            Console.WriteLine("main thrad sleeping for 1 second...");
            Thread.Sleep(1000);

            Console.WriteLine("main thread signaling worker thread...");
            mEvent.Set();
        }
    }
}

このときコードがすっきりしているのではないでしょうか.ここでは、上のAutoResetEventをManualResetEventに変えるのも問題ありませんが、その違いは何ですか.個人的に最大の違いは、AutoResetEventがスレッドをアクティブにすると、その状態が終了から非終了に自動的に変化することです.逆に、ManualResetEventは、Resetメソッドが呼び出された場合にのみ非終了状態に復元される任意の複数のスレッドを終了状態でアクティブ化することを可能にする.次のコードを開きます.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace AutoResetEventTest
{
    class Class3
    {
        static AutoResetEvent mEvent = new AutoResetEvent(false);
        //static ManualResetEvent mEvent = new ManualResetEvent(false);

        static void DoWork()
        {
            Console.WriteLine("   worker thread started, now waiting on event...");
            for (int i = 0; i < 3; i++)
            {
                mEvent.WaitOne();
                //mEvent.Reset();
                Console.WriteLine("   worker thread reactivated, now exiting...");
            }
        }

        static void Main()
        {
            Console.WriteLine("main thread starting worker thread...");
            Thread t = new Thread(DoWork);
            t.Start();

            for (int i = 0; i < 3; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("main thread signaling worker thread...");
                mEvent.Set();
            }
        }
    }
}
AutoResetEventだけをManualResetEventに変えようとすると、出力が乱れていることに気づきます.なぜですか.
もしそうならWaitOne()とmanualevent.WaitOne()は,スレッドが信号を得た後も実行を継続する.差は呼び出し後WaitOne()は、1つのスレッドのみを許可し、あるスレッドが信号(すなわち、他のスレッドがautoevent.Set()メソッドを呼び出した後)を得ると、autoeventは自動的に信号を送信しない状態にし、他のWaitOneを呼び出すスレッドは待機し続けるしかない.すなわち、autoeventは一度に1つのスレッドのみを起動する.一方、manualeventは複数のスレッドを起動することができ、あるスレッドがsetメソッドを呼び出すと、他のwaitoneを呼び出すスレッド取得信号が実行され続け、manualeventは自動的に信号を送信しないように設定することはできない.すなわち、手動でmanualeventを呼び出す場合を除く.Reset()メソッドでは、manualeventは常に信号状態を維持し、manualeventは複数のスレッドを同時に起動して実行を継続することができます.
上記のコードでAutoResetEventをManualResetEventに置き換えると、waitoneの後ろにresetを作るだけで同じ効果が得られます.
次に簡単な例を示します.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace AutoResetEventTest
{
    class Class4
    {
        public static AutoResetEvent mEvent = new AutoResetEvent(false);

        public static void trmain()
        {
            Thread tr = Thread.CurrentThread;
            Console.WriteLine("thread: waiting for an event");
            mEvent.WaitOne();
            Console.WriteLine("thread: got an event");
            for (int x = 0; x < 10; x++)
            {
                Thread.Sleep(1000);
                Console.WriteLine(tr.Name + ": " + x);
            }
        }
        static void Main(string[] args)
        {
            Thread thrd1 = new Thread(new ThreadStart(trmain));
            thrd1.Name = "thread1";
            thrd1.Start();
            for (int x = 0; x < 10; x++)
            {
                Thread.Sleep(900);
                Console.WriteLine("Main:" + x);
                if (5 == x) mEvent.Set();
            }
            while (thrd1.IsAlive)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Main: waiting for thread to stop");
            }
        }
    }
}

もっと感じたの?その後、他のいくつかを見てみましょう.
System.Threading.WaitHandle.WaitOneは、単一のイベントが終了状態になるまでスレッドを待機させます.
System.Threading.WaitHandle.WaitAnyは、1つ以上の指示されたイベントが終了状態になるまでスレッドをブロックする.
System.Threading.WaitHandle.WaitAllは、すべての指示されたイベントが終了状態になるまでスレッドをブロックします.
次に、WaitAllの使用例を示します.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace AutoResetEventTest
{
    class other
    {
        static void Main(string[] args)
        {
            Random randomGenerator = new Random();
            AutoResetEvent[] resets=new AutoResetEvent[5];

            for (int i = 0; i < 5; i++)
            {
                resets[i] = new AutoResetEvent(false);
                int wTime = randomGenerator.Next(10)+1;

                worker w = new worker(wTime, resets[i]);

                Thread thrd1 = new Thread(new ThreadStart(w.work));
                thrd1.Start();  
            }
            WaitHandle.WaitAll(resets);
            Console.WriteLine("ALL worker done - main exiting.");
        }

    }

    public class worker
    {
        public string name;
        public int wTime;
        public AutoResetEvent mEvent;

        public worker(int w, AutoResetEvent m)
        {
            name = w.ToString();
            wTime = w * 1000;
            mEvent = m;
        }

        public void work()
        {
            Console.WriteLine(name + " worker thread waiting for " + wTime + "....");
            Thread.Sleep(wTime);
            Console.WriteLine(name + " worker thread back...");
            mEvent.Set();
        }
    }
}

簡単に言えば、5つのスレッドを開いて、各スレッドはランダムに数秒休眠して、すべて完成した後にメインスレッドに退出することを通知して、ここでAutoResetEvent配列を開いて、メインスレッドはWaitHandle.WaitAll(resets)では、サブスレッドがスリープした後にSet 1個のAutoResetEventがあり、最後にSetが終わった後、メインスレッドは下に実行されます.最後に本を買ってお金を払って品物を受け取る例をもう一つあげて、理解を深めます.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace AutoResetEventTest
{
    class Program
    {
        const int numIterations = 10;
        static AutoResetEvent myResetEvent = new AutoResetEvent(false);
        static AutoResetEvent ChangeEvent = new AutoResetEvent(false);
        //static ManualResetEvent myResetEvent = new ManualResetEvent(false);
        //static ManualResetEvent ChangeEvent = new ManualResetEvent(false);
        static int number; // 

        static void Main()
        {
            Thread payMoneyThread = new Thread(new ThreadStart(PayMoneyProc));
            payMoneyThread.Name = " ";
            Thread getBookThread = new Thread(new ThreadStart(GetBookProc));
            getBookThread.Name = " ";
            payMoneyThread.Start();
            getBookThread.Start();

            for (int i = 1; i <= numIterations; i++)
            {
                Console.WriteLine(" : {0}", i);
                number = i;
                //Signal that a value has been written.
                myResetEvent.Set();
                //ChangeEvent.Set();
                Thread.Sleep(10);
            }
            payMoneyThread.Abort();
            getBookThread.Abort();
        }

        static void PayMoneyProc()
        {
            while (true)
            {
                myResetEvent.WaitOne();
                //myResetEvent.Reset();
                Console.WriteLine("{0}: {1}", Thread.CurrentThread.Name, number);
                ChangeEvent.Set();
            }
        }
        static void GetBookProc()
        {
            while (true)
            {
                ChangeEvent.WaitOne();
                //ChangeEvent.Reset();               
                Console.WriteLine("{0}: {1}", Thread.CurrentThread.Name, number);
                Console.WriteLine("------------------------------------------");
                //Thread.Sleep(0);
            }
        }
    }
}

本文の一部の資料はネットに由来して、特にここで声明します!
関連リンク:http://www.cnblogs.com/freshman0216/archive/2008/07/30/1252345.html
http://msdn.microsoft.com/zh-cn/library/z6w25xa6%28VS.80%29.aspx