文章出處

 接上文 多線程編程學習筆記——線程同步(一)

四、使用AutoResetEvent

1. 使用AutoResetEvent類來實現從一個線程向另一個線程發出通知。

2.代碼如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; //引入線程
using System.Diagnostics;

namespace ThreadSynchronousDemo
{
    class Program
    {

        static AutoResetEvent autoResetWork = new AutoResetEvent(false);
        static AutoResetEvent autoResetMain = new AutoResetEvent(false);
        static void Main(string[] args)
        {

            Console.WriteLine("開始,AutoResetEvent 同步");       

                string threadName = "線程 1";         
                var t = new Thread((() => working(threadName, 10)));

                t.Start();
            Console.WriteLine("開始,第一次工作");
 

            autoResetWork.WaitOne();//萬事俱備只欠東風,事情卡在這里了,

            Console.WriteLine("第一次工作完成");
            Console.WriteLine("主線程操作,準備發信號");
            Thread.Sleep(TimeSpan.FromSeconds(5));
            //發信號,說明值已經被寫進去了。這里的意思是說Set是一個發信號的方法。

            autoResetMain.Set();
            Console.WriteLine("現在運行第二次工作。");
            autoResetWork.WaitOne();
            Console.WriteLine("第二次工作完成");
            Console.Read();
        } 

        static void working(string name,int seconds)
        {
            Console.WriteLine("{0} 開始運行工作", name);
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine("{0}  正在工作。。。。。。",
              name);
            //發信號,說明值已經被寫進去了。這里的意思是說Set是一個發信號的方法。

            autoResetWork.Set();
            Console.WriteLine("等待主線程完成工作,并發出信號");
            autoResetMain.WaitOne();
            Console.WriteLine("主線程發來信號,開始第二次工作");
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine("{0} 第二次工作正在進行中。。。。。", name);
            autoResetWork.Set();
        }
    }
}
 

3.程序運行結果,如下圖。

 

         以上程序中,我們定義了兩個AutoResetEvent實例。其中一個是從子線程往主線程發信號 ,另一個是主線程往子線程發信號。我們在構造AutoResetEvent時,傳入了false,定義了這兩個實例的初始狀態unsignaled。這個狀態下,任何線程調用這兩個實例的WaitOne方法將會被阻塞,直到我們調用了Set方法。如果我們在構造的時候傳入了true,則這兩個實例的初始狀態是singnaled,則線程調用WaitOne則會被立即處理。

 

五、使用ManualResetEventSlim類

1. 使用ManualResetEventSlim在線程間傳遞信號。

 2.代碼如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; //引入線程
using System.Diagnostics; 

namespace ThreadSynchronousDemo
{
    class Program
    {
        static ManualResetEventSlim manuResetWork = new ManualResetEventSlim(false);      

        static void Main(string[] args)
        {
            Console.WriteLine("開始,ManualResetEventSlim 同步");       

                string threadName = "線程 1";
            string threadName2 = "線程 2";
 

            string threadName3 = "線程 3";
            var t = new Thread((() => working(threadName, 3)));
            var t2 = new Thread((() => working(threadName2, 6)));

            var t3 = new Thread((() => working(threadName3, 12)));

            t.Start();
            t2.Start();
            t3.Start();
            Thread.Sleep(TimeSpan.FromSeconds(5));
            Console.WriteLine("開始,打開 線程工作大門");
            manuResetWork.Set(); //發信號
            Thread.Sleep(TimeSpan.FromSeconds(3));
            manuResetWork.Reset();
           

            Console.WriteLine("線程工作大門,關閉");         

            Thread.Sleep(TimeSpan.FromSeconds(10));
            Console.WriteLine("打開線程工作大門第二次打開了");
            manuResetWork.Set(); //發信號
            Thread.Sleep(TimeSpan.FromSeconds(3));
            manuResetWork.Reset();
            Console.WriteLine("線程工作大門,又關閉了");
            Console.Read();
        }

        static void working(string name,int seconds)
        {

            Console.WriteLine("{0} 休息", name);
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine("{0}  等待打開線程運行的大門",
              name);
            manuResetWork.Wait();
            Console.WriteLine("線程運行的大門打開了,{0} 進行工作", name);       

        }
    }
}

3.程序運行結果,如下圖。

      當主程序啟動時,首先創建ManualResetEvenSlim類的一個實例,然后啟動了三個線程,等待事件信號通知它們繼續工作。

      ManualResetEvenSlim的工作方式有點像人群通過大門,而AutoResetEvent事件像一個旋轉門,一次只能通過一人。

      ManualResetEvenSlim打開了大門,一直保持打開,直到調用了Reset方法。直到再次調用Set方法打開 大門。

 

六、使用CountDownEvent類

1. 使用CountDownEvent信號類來等待直到一定數量的操作完成。

2.代碼如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; //引入線程
using System.Diagnostics; 

namespace ThreadSynchronousDemo
{
    class Program
    {
        static CountdownEvent CountDownWork = new CountdownEvent(2);      

        static void Main(string[] args)
        {

            Console.WriteLine("開始,CountdownEvent 同步");       

            var t = new Thread((() => working("第 1 個工作線程任務", 3)));
            var t2 = new Thread((() => working("第 2 個工作線程任務", 6)));

            //var t3 = new Thread((() => working("第 3 個工作線程任務", 12)));
            t.Start();
            t2.Start();
            //t3.Start();
            Thread.Sleep(TimeSpan.FromSeconds(5));
            Console.WriteLine("開始,線程工作計數");
            CountDownWork.Wait();           

            Console.WriteLine("計數完成,2個工作 已經完成!");
            //如果把上面代碼注釋的第三個線程還原,釋放對象,可以造成第三個線程的拋出錯誤

            CountDownWork.Dispose();     

            Console.Read();
        } 

        static void working(string message,int seconds)
        {
            Console.WriteLine("工作前休息 {0}",DateTime.Now.Second);
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine(message);          

            CountDownWork.Signal();
            Console.WriteLine("發出計數信號, 工作已經完成一項");      

        }
    }
}

 

3.程序運行結果如下圖。

     程序啟動時,創建了一個CountDownEven實例,在構造中指定了,當兩個操作完成時給出信號。然后我們啟動了兩個線程進行工作,當第二個線程完成操作時,主線程從等待CountDownEvent的狀態中返回并繼續工作。這個類使用場景是,主線程需要等待多個線程完成工作之后,才能繼續的情形。

缺點:必須要等待指定數量的線程全部完成工作,否則就一直會等待,請確保使用CountDownEvent時,所有線程完成工作后,都要調用Signal方法。

說明:

1)  把上面代碼中注釋的第三個線程的代碼,還原則會出現以下錯誤。

 

2) 如果把 第三個線程啟用,同時把 CountDownWork.Dispose();注釋,則會出現以下錯誤信息。

 

 

 

 

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()