查看: 175|回复: 0

[.NET源码] 多线程编程学习笔记——线程同步(二)

发表于 5 天前
太阳http代理AD
接上文 多线程编程学习笔记——线程同步(一) 四、使用AutoResetEvent

1. 使用AutoResetEvent类来实现从一个线程向另一个线程发出通知。

2.代码如下

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading; //引入线程
  6. using System.Diagnostics;
  7. namespace ThreadSynchronousDemo
  8. {
  9. class Program
  10. {
  11. static AutoResetEvent autoResetWork = new AutoResetEvent(false);
  12. static AutoResetEvent autoResetMain = new AutoResetEvent(false);
  13. static void Main(string[] args)
  14. {
  15. Console.WriteLine("开始,AutoResetEvent 同步");
  16. string threadName = "线程 1";
  17. var t = new Thread((() => working(threadName, 10)));
  18. t.Start();
  19. Console.WriteLine("开始,第一次工作");
  20. autoResetWork.WaitOne();//万事俱备只欠东风,事情卡在这里了,
  21. Console.WriteLine("第一次工作完成");
  22. Console.WriteLine("主线程操作,准备发信号");
  23. Thread.Sleep(TimeSpan.FromSeconds(5));
  24. //发信号,说明值已经被写进去了。这里的意思是说Set是一个发信号的方法。
  25. autoResetMain.Set();
  26. Console.WriteLine("现在运行第二次工作。");
  27. autoResetWork.WaitOne();
  28. Console.WriteLine("第二次工作完成");
  29. Console.Read();
  30. }
  31. static void working(string name,int seconds)
  32. {
  33. Console.WriteLine("{0} 开始运行工作", name);
  34. Thread.Sleep(TimeSpan.FromSeconds(seconds));
  35. Console.WriteLine("{0} 正在工作。。。。。。",
  36. name);
  37. //发信号,说明值已经被写进去了。这里的意思是说Set是一个发信号的方法。
  38. autoResetWork.Set();
  39. Console.WriteLine("等待主线程完成工作,并发出信号");
  40. autoResetMain.WaitOne();
  41. Console.WriteLine("主线程发来信号,开始第二次工作");
  42. Thread.Sleep(TimeSpan.FromSeconds(seconds));
  43. Console.WriteLine("{0} 第二次工作正在进行中。。。。。", name);
  44. autoResetWork.Set();
  45. }
  46. }
  47. }
复制代码

3.程序运行结果,如下图。

以上程序中,我们定义了两个AutoResetEvent实例。其中一个是从子线程往主线程发信号 ,另一个是主线程往子线程发信号。我们在构造AutoResetEvent时,传入了false,定义了这两个实例的初始状态unsignaled。这个状态下,任何线程调用这两个实例的WaitOne方法将会被阻塞,直到我们调用了Set方法。如果我们在构造的时候传入了true,则这两个实例的初始状态是singnaled,则线程调用WaitOne则会被立即处理。

五、使用ManualResetEventSlim类

1. 使用ManualResetEventSlim在线程间传递信号。

2.代码如下

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading; //引入线程
  6. using System.Diagnostics;
  7. namespace ThreadSynchronousDemo
  8. {
  9. class Program
  10. {
  11. static ManualResetEventSlim manuResetWork = new ManualResetEventSlim(false);
  12. static void Main(string[] args)
  13. {
  14. Console.WriteLine("开始,ManualResetEventSlim 同步");
  15. string threadName = "线程 1";
  16. string threadName2 = "线程 2";
  17. string threadName3 = "线程 3";
  18. var t = new Thread((() => working(threadName, 3)));
  19. var t2 = new Thread((() => working(threadName2, 6)));
  20. var t3 = new Thread((() => working(threadName3, 12)));
  21. t.Start();
  22. t2.Start();
  23. t3.Start();
  24. Thread.Sleep(TimeSpan.FromSeconds(5));
  25. Console.WriteLine("开始,打开 线程工作大门");
  26. manuResetWork.Set(); //发信号
  27. Thread.Sleep(TimeSpan.FromSeconds(3));
  28. manuResetWork.Reset();
  29. Console.WriteLine("线程工作大门,关闭");
  30. Thread.Sleep(TimeSpan.FromSeconds(10));
  31. Console.WriteLine("打开线程工作大门第二次打开了");
  32. manuResetWork.Set(); //发信号
  33. Thread.Sleep(TimeSpan.FromSeconds(3));
  34. manuResetWork.Reset();
  35. Console.WriteLine("线程工作大门,又关闭了");
  36. Console.Read();
  37. }
  38. static void working(string name,int seconds)
  39. {
  40. Console.WriteLine("{0} 休息", name);
  41. Thread.Sleep(TimeSpan.FromSeconds(seconds));
  42. Console.WriteLine("{0} 等待打开线程运行的大门",
  43. name);
  44. manuResetWork.Wait();
  45. Console.WriteLine("线程运行的大门打开了,{0} 进行工作", name);
  46. }
  47. }
  48. }
复制代码

3.程序运行结果,如下图。

当主程序启动时,首先创建ManualResetEvenSlim类的一个实例,然后启动了三个线程,等待事件信号通知它们继续工作。

ManualResetEvenSlim的工作方式有点像人群通过大门,而AutoResetEvent事件像一个旋转门,一次只能通过一人。

ManualResetEvenSlim打开了大门,一直保持打开,直到调用了Reset方法。直到再次调用Set方法打开 大门。

六、使用CountDownEvent类

1. 使用CountDownEvent信号类来等待直到一定数量的操作完成。

2.代码如下

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading; //引入线程
  6. using System.Diagnostics;
  7. namespace ThreadSynchronousDemo
  8. {
  9. class Program
  10. {
  11. static CountdownEvent CountDownWork = new CountdownEvent(2);
  12. static void Main(string[] args)
  13. {
  14. Console.WriteLine("开始,CountdownEvent 同步");
  15. var t = new Thread((() => working("第 1 个工作线程任务", 3)));
  16. var t2 = new Thread((() => working("第 2 个工作线程任务", 6)));
  17. //var t3 = new Thread((() => working("第 3 个工作线程任务", 12)));
  18. t.Start();
  19. t2.Start();
  20. //t3.Start();
  21. Thread.Sleep(TimeSpan.FromSeconds(5));
  22. Console.WriteLine("开始,线程工作计数");
  23. CountDownWork.Wait();
  24. Console.WriteLine("计数完成,2个工作 已经完成!");
  25. //如果把上面代码注释的第三个线程还原,释放对象,可以造成第三个线程的抛出错误
  26. CountDownWork.Dispose();
  27. Console.Read();
  28. }
  29. static void working(string message,int seconds)
  30. {
  31. Console.WriteLine("工作前休息 {0}",DateTime.Now.Second);
  32. Thread.Sleep(TimeSpan.FromSeconds(seconds));
  33. Console.WriteLine(message);
  34. CountDownWork.Signal();
  35. Console.WriteLine("发出计数信号, 工作已经完成一项");
  36. }
  37. }
  38. }
复制代码

3.程序运行结果如下图。

程序启动时,创建了一个CountDownEven实例,在构造中指定了,当两个操作完成时给出信号。然后我们启动了两个线程进行工作,当第二个线程完成操作时,主线程从等待CountDownEvent的状态中返回并继续工作。这个类使用场景是,主线程需要等待多个线程完成工作之后,才能继续的情形。

缺点:必须要等待指定数量的线程全部完成工作,否则就一直会等待,请确保使用CountDownEvent时,所有线程完成工作后,都要调用Signal方法。

说明:

1) 把上面代码中注释的第三个线程的代码,还原则会出现以下错误。

2) 如果把 第三个线程启用,同时把 CountDownWork.Dispose();注释,则会出现以下错误信息。



太阳http代理AD
回复

使用道具 举报