程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 線程的信號機制,線程信號機制

線程的信號機制,線程信號機制

編輯:C#入門知識

線程的信號機制,線程信號機制


摘自:http://www.cnblogs.com/willick/p/4177977.html  僅供參考學習

有時候你需要一個線程在接收到某個信號時,才開始執行,否則處於等待狀態,這是一種基於信號的事件機制。.NET框架提供一個ManualResetEvent類來處理這類事件,它的 WaiOne 實例方法可使當前線程一直處於等待狀態,直到接收到某個信號。它的Set方法用於打開發送信號。下面是一個信號機制的使用示例:

 1  //線程的信號機制
 2             #region
 3             var signal = new ManualResetEvent(false);
 4             DateTime beginTime = DateTime.Now;
 5             new Thread(() => {
 6                 Console.WriteLine("waiting for signal...");
 7                 signal.WaitOne();
 8                 signal.Dispose();
 9                 Console.WriteLine("Got signal");
10             }).Start();
11             Thread.Sleep(2000);
12             TimeSpan ts = (DateTime.Now - beginTime);
13             Console.WriteLine("已消耗啦"+ts.TotalMilliseconds);
14             signal.Set();
15 
16             Console.ReadKey();
17             #endregion

效果:

1 waiting for signal...
2 已消耗啦2003.1145
3 Got signal

當執行Set方法後,信號保持打開狀態,可通過Reset方法將其關閉,若不再需要,通過Dispose將其釋放。如果預期的等待時間很短,可以用ManualResetEventSlim代替ManualResetEvent,前者在等待時間較短時性能更好。信號機制非常有用,後面的日志案例會用到它。

 線程池中的線程

線程池中的線程是有CLR來管理的,在下面的2中條件下,線程池能起到最好的效用:

*任務運行的時候比較短(<250ms),這樣CLR可以充分調配現有的空閒線程來處理該任務;

*大量時間處於等待(或阻塞)的任務不去支配線程池的線程。

1 // 方式1:Task.Run,.NET Framework 4.5 才有
2 Task.Run (() => Console.WriteLine ("Hello from the thread pool"));
3 
4 // 方式2:ThreadPool.QueueUserWorkItem
5 ThreadPool.QueueUserWorkItem (t => Console.WriteLine ("Hello from the thread pool"));

 案例:支持並發的異步日志組件

基於上面的知識,我們可以實現應用程序的並發寫日志日志功能。在應用程序中,寫日志是常見的功能,簡單分析一下該功能的需求:

   

 1  public class Logger
 2     {
 3         /*
 4          * 1.需要一個用來存放寫日志任務的隊列
 5          * 2.需要有一個信號機制來標識是否有新的任務要執行
 6          * 3.當有新的寫日志任務時,將該任務加入到隊列中,並發出信號
 7          * 4.用一個方法來處理隊列中的任務,當接受新任務信號時,就依次調用隊列中的任務
 8          * 5.lock對象要實現對入棧和出棧的鎖操作,保證出棧的時候不會有入棧的操作
 9          */
10         private Queue<Action> queue;
11         private ManualResetEvent switchSignal;
12         private Thread loggingThread;
13         private static readonly Logger log = new Logger();
14         public static Logger GetIntence()
15         {
16             return log;
17         }
18         private Logger()
19         {
20             queue = new Queue<Action>();
21             switchSignal = new ManualResetEvent(false);
22             loggingThread = new Thread(ReceiveInfo);
23             loggingThread.IsBackground = true;
24             loggingThread.Start();
25         }
26         private void ReceiveInfo()
27         {
28             switchSignal.WaitOne();
29             switchSignal.Reset();
30 
31             Thread.Sleep(100);
32             Queue<Action> oldQueue;
33             lock (queue)
34             { 
35                 oldQueue = new Queue<Action>(queue);
36                 queue.Clear();
37             }
38             foreach (var action in oldQueue)
39             {
40                 action();
41             }
42         }
43         //任務添加
44         public void WriteLog(string content)
45         {
46             lock (queue)//存在線程安全問題,可能發生阻塞。
47             {
48                 queue.Enqueue(() => File.AppendAllText("log.txt", content));
49             }
50             switchSignal.Set();
51         }
52         public static void BeginLog(string content)
53         {
54             Task.Factory.StartNew(() => GetIntence().WriteLog(content));//4.0  不支持task.run();
55         }
56     }
 1  static void Main(string[] args)
 2         {
 3             Thread t1 = new Thread(Working);
 4             t1.Name = "Thread1";
 5             Thread t2 = new Thread(Working);
 6             t2.Name = "Thread2";
 7             Thread t3 = new Thread(Working);
 8             t3.Name = "Thread3";
 9 
10             // 依次啟動3個線程。
11             t1.Start();
12             t2.Start();
13             t3.Start();
14 
15             Console.ReadKey();
16         }
17 
18         // 每個線程都同時在工作
19         static void Working()
20         {
21             // 模擬1000次寫日志操作
22             for (int i = 0; i < 1000; i++)
23             {
24                 //  異步寫文件
25                 Logger.BeginLog(Thread.CurrentThread.Name + " writes a log: " + i + ", on " + DateTime.Now.ToString() + "\r\n");
26             }
27         }
28     }

通過這個示例,目的是讓大家掌握線程和並發在開發中的基本應用和要注意的問題。

遺憾的是這個Logger類並不完美,而且存在線程安全問題(代碼中用紅色字體標出),雖然實際環境概率很小。可能上面代碼多次運行都很難看到有異常發生(我多次運行未發生異常),但同時再添加幾個線程可能就會有問題了。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved