• C#多线程之线程同步篇2


      在上一篇C#多线程之线程同步篇1中,我们主要学习了执行基本的原子操作、使用Mutex构造以及SemaphoreSlim构造,在这一篇中我们主要学习如何使用AutoResetEvent构造、ManualResetEventSlim构造和CountDownEvent构造。

    四、使用AutoResetEvent构造

      在这一小节中,我们将学习如何使用AutoResetEvent构造从一个线程向另一个线程发送通知。AutoResetEvent通知一个等待线程某个事件已经发生。具体步骤如下所示:

    1、使用Visual Studio 2015创建一个新的控制台应用程序。

    2、双击打开“Program.cs”文件,编写代码如下所示:

     1 using System;
     2 using System.Threading;
     3 using static System.Console;
     4 using static System.Threading.Thread;
     5 
     6 namespace Recipe04
     7 {
     8     class Program
     9     {
    10         private static AutoResetEvent workerEvent = new AutoResetEvent(false);
    11         private static AutoResetEvent mainEvent = new AutoResetEvent(false);
    12 
    13         static void Process(int seconds)
    14         {
    15             WriteLine("Starting a long running work...");
    16             Sleep(TimeSpan.FromSeconds(seconds));
    17             WriteLine("Work is done!");
    18             workerEvent.Set();
    19             WriteLine("Waiting for a main thread to complete its work");
    20             mainEvent.WaitOne();
    21             WriteLine("Starting second operation...");
    22             Sleep(TimeSpan.FromSeconds(seconds));
    23             WriteLine("Work is done!");
    24             workerEvent.Set();
    25         }
    26 
    27         static void Main(string[] args)
    28         {
    29             var t = new Thread(() => Process(10));
    30             t.Start();
    31 
    32             WriteLine("Waiting for another thread to complete work");
    33             workerEvent.WaitOne();
    34             WriteLine("First operation is completed!");
    35             WriteLine("Performing an operation on a main thread");
    36             Sleep(TimeSpan.FromSeconds(5));
    37             mainEvent.Set();
    38             WriteLine("Now running the second operation on a second thread");
    39             workerEvent.WaitOne();
    40             WriteLine("Second operation is completed!");
    41         }
    42     }
    43 }

    3、运行该控制台应用程序,运行效果(每次运行效果可能不同)如下图所示:

      在第10~11行代码处,我们定义了两个AutoResetEvent实例:workerEvent和mainEvent。workerEvent用于从新建线程中向主线程发送通知,mainEvent用于从主线程向新建线程发送通知。在调用AutoResetEvent的构造方法的时候,我们给该构造方法的“initialState”参数传递了false值,指定AutoResetEvent实例的初始状态为“无信号状态”,这意味着调用AutoResetEvent实例的“WaitOne”方法的线程将会被阻塞,直到我们调用AutoResetEvent实例的“Set”方法之后,该线程才会继续执行。如果我们将AutoResetEvent类的构造方法的“initialState”参数值设置为true,则AutoResetEvent实例的初始状态为“信号状态”,那么第一个调用AutoResetEvent实例的“WaitOne”方法的线程将会被立即执行,然后AutoResetEvent实例的状态自动变为“无信号状态”,这个时候,当我们再次调用AutoResetEvent的“WaitOne”方法后,必须在另一个线程中调用AutoResetEvent的“Set”方法才能继续执行当前的线程。

      在第29行代码处,我们创建了一个新的线程用于执行“Process”方法,并在第30行代码处启动线程。

      在第33行代码处,我们调用AutoResetEvent实例workerEvent的“WaitOne”方法,导致主线程被阻塞,然而在我们在第29行代码处创建的线程中,我们调用了AutoResetEvent实例WorkerEvent的“Set”方法,因此,主线程得以继续执行。当执行到第20行代码处,我们在新建线程中调用了AutoResetEvent实例mainEvent的“WaitOne”方法,因此导致新建线程被阻塞,然而在主线程执行到第37行代码处,我们调用了AutoResetEvent实例mainEvent的“Set”方法,因此,新建线程得以继续执行。而主线程在执行到第39行代码处,主线程又被阻塞,而新建线程执行到第24行代码处,导致主线程得以继续执行,因此,主线程执行到第40行代码,控制台应用程序正常结束。

      AutoResetEvent是kernel-time构造,因此,如果没有必要,我们建议使用下一节介绍的ManualResetEventslim来替代AutoResetEvent。

    五、使用ManualResetEventSlim构造

      在这一小节中,我们将学习如何使用ManualResetEventSlim构造在多个线程之间更加灵活地发送通知。具体步骤如下所示:

    1、使用Visual Studio 2015创建一个新的控制台应用程序。

    2、双击打开“Program.cs”文件,编写代码如下所示:

     1 using System;
     2 using System.Threading;
     3 using static System.Console;
     4 using static System.Threading.Thread;
     5 
     6 namespace Recipe05
     7 {
     8     class Program
     9     {
    10         private static ManualResetEventSlim mainEvent = new ManualResetEventSlim(false);
    11 
    12         static void TravelThroughGates(string threadName, int seconds)
    13         {
    14             WriteLine($"{threadName} falls to sleep");
    15             Sleep(TimeSpan.FromSeconds(seconds));
    16             WriteLine($"{threadName} waits for the gates to open!");
    17             mainEvent.Wait();
    18             WriteLine($"{threadName} enters the gates!");
    19         }
    20         
    21         static void Main(string[] args)
    22         {
    23             var t1 = new Thread(() => TravelThroughGates("Thread 1", 5));
    24             var t2 = new Thread(() => TravelThroughGates("Thread 2", 6));
    25             var t3 = new Thread(() => TravelThroughGates("Thread 3", 12));
    26 
    27             t1.Start();
    28             t2.Start();
    29             t3.Start();
    30 
    31             Sleep(TimeSpan.FromSeconds(6));
    32             WriteLine("The gates are now open!");
    33             mainEvent.Set();
    34             Sleep(TimeSpan.FromSeconds(2));
    35             mainEvent.Reset();
    36             WriteLine("The gates have been closed!");
    37             Sleep(TimeSpan.FromSeconds(10));
    38             WriteLine("The gates are now open for the second time!");
    39             mainEvent.Set();
    40             Sleep(TimeSpan.FromSeconds(2));
    41             WriteLine("The gates have been closed!");
    42             mainEvent.Reset();
    43         }
    44     }
    45 }

    3、运行该控制台应用程序,运行效果(每次运行效果可能不同)如下图所示:

      在第10行代码处,我们定义了一个ManualResetEventSlim类型的实例mainEvent,并给它的构造方法的“initialState”参数传递了false值,表示该对象的初始状态为“无信号状态”。

      在第23~25行代码处,我们创建了三个线程t1、t2和t3。这三个线程都用于执行“TraveThroughGates”方法,在该方法的内部,我们调用了ManualResetEventSlim实例mainEvent的“Wait”方法,以阻塞t1、t2和t3线程的执行。

      在第31行代码处,我们让主线程阻塞6秒钟,在这六秒钟内,线程t1和t2都执行到第17行代码处,这个时候线程t1和t2都阻塞,并且等待mainEvent的“Set”方法被调用,以接收信号后继续执行。主线程阻塞6秒钟后,会执行第33行代码,执行完毕这行代码之后,线程t1和t2都会接收到通知,因此,线程t1和t2都会继续往下执行,从而都执行第18行代码,之后线程t1和t2执行完毕,结束。

      由于线程t3在主线程执行到第33行代码处的时候,还在阻塞(因为执行了第15行代码)中,因此线程t3在主线程执行到第33行代码处的时候不受影响,继续阻塞。

      当主线程执行到第34行代码处的时候,线程t3依然在阻塞状态中。在主线程执行了第35行代码之后,mainEvent被重置为“无信号状态”。当主线程执行到第37行代码处,主线程被阻塞10秒钟。在主线程被阻塞的10秒钟内,线程t3会执行到第17行代码处,从而t3线程被阻塞,等待通知的到来,才能继续执行。

      当主线程阻塞10秒钟之后,会执行第39行代码,从而导致线程t3继续执行,因此会执行第18行代码,线程t3结束。

      然后主线程阻塞2秒钟后,又将mainEvent重置为“无信号状态”,然后主线程结束。

    六、使用CountdownEvent构造

      在这一小节中,我们将学习如何使用CountdownEvent构造等待发送一定数量的通知后,才继续执行被阻塞的线程。学习步骤如下所示:

    1、使用Visual Studio 2015创建一个新的控制台应用程序。

    2、双击打开“Program.cs”文件,编写代码如下所示:

     1 using System;
     2 using System.Threading;
     3 using static System.Console;
     4 using static System.Threading.Thread;
     5 
     6 namespace Recipe06
     7 {
     8     class Program
     9     {
    10         private static CountdownEvent countdown = new CountdownEvent(2);
    11 
    12         static void PerformOperation(string message, int seconds)
    13         {
    14             Sleep(TimeSpan.FromSeconds(seconds));
    15             WriteLine(message);
    16             countdown.Signal();
    17         }
    18 
    19         static void Main(string[] args)
    20         {
    21             WriteLine("Starting two operations");
    22             var t1 = new Thread(() => PerformOperation("Operation 1 is completed", 4));
    23             var t2 = new Thread(() => PerformOperation("Operation 2 is completed", 8));
    24 
    25             t1.Start();
    26             t2.Start();
    27             countdown.Wait();
    28             WriteLine("Both operations have been completed.");
    29             countdown.Dispose();
    30         }
    31     }
    32 }

    3、运行该控制台应用程序,运行效果如下图所示:

      在第10行代码处,我们创建了一个CountdownEvent的实例countdown,并给该构造方法的“initialCount”参数传递了数值2,表示我们希望等待2个通知发送完毕后,被阻塞的线程才能继续执行。

      在第22~23行代码处,我们创建了两个新线程用于执行“PerformOperation”方法,在该方法中,我们调用了countdown的“Signal”方法,用于发送通知,并减小CountdownEvent的CurrentCount的值,当CurrentCount的值减少到0时,被阻塞的线程才能继续执行。

      在第27行代码处,我们在主线程中调用了countdown的“Wait”方法,从而主线程被阻塞,直到接收到通知并且CurrentCount的值为0时,主线程才能继续执行。

      注意,如果将第10行代码处的2修改为3,再次运行该程序,主线程会一直等待,不会结束,因为CurrentCount的值没有减少到0。

  • 相关阅读:
    Atitit.ati orm的设计and架构总结 适用于java c# php版
    Atitit.ati dwr的原理and设计 attilax 总结 java php 版本
    Atitit.ati dwr的原理and设计 attilax 总结 java php 版本
    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全
    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全
    Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结
    Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结
    atitit.基于  Commons CLI 的命令行原理与 开发
    atitit.基于  Commons CLI 的命令行原理与 开发
    atitit.js 与c# java交互html5化的原理与总结.doc
  • 原文地址:https://www.cnblogs.com/yonghuacui/p/6208381.html
Copyright © 2020-2023  润新知