详解C# 线程的挂起与唤醒

作者:jack_Meng 时间:2023-03-12 12:40:02 

     如果说C#和C++有什么不同,博主不得不说,对于异步的支持程度是C#的一一个伟大的进步。

    其实早期的C++都没有异步,并发的概念。博主第一次使用C++创建异步程序的时候,是使用boost库的内容进行实现的。相对而言,C#对于异步的支持可以说是相当的好。相信很多名词大家都很耳熟能详,比如说Thread,BeginInvoke,Delegate,backgroundworker等等。。。其实楼主在使用了这么多的异步操作过程中,还是觉得backgroudworker比较好用。

    当然,我们今天要说的和上面的无关。讲述的是如何在线程中进行挂起唤醒操作。

    假设,有一个Thread现在需要挂起,等到合适的时候再唤醒那么这个线程(消费者模式)。如果大家需要用Suspend,Resume操作,我建议还是要思考再三。以下是msdn原话(https://msdn.microsoft.com/zh-cn/library/system.threading.thread.suspend(v=vs.110).aspx):

    Do not use the Suspend and Resume methods to synchronize the activities of threads. You have no way of knowing what code a thread is executing when you suspend it. If you suspend a thread while it holds locks during a security permission evaluation, other threads in the AppDomain might be blocked. If you suspend a thread while it is executing a class constructor, other threads in the AppDomain that attempt to use that class are blocked. Deadlocks can occur very easily.

     本篇文章要说的线程挂起与继续的方式其实是利用AutoResetEvent和ManualResetEvent的方法进行堵塞和继续的。

在介绍AutoResetEvent和ManualResetEvent之前,先介绍一个概念,就是线程中Set()和Reset()的区别。

  • set:指的是将一个事件设置为有信号,那么被这个事件堵塞的线程就会继续下去。

  • reset:指的是将一个事件设置为无信号,那么尝试继续的事件就会被堵塞。

一,AutoResetEvent类

     这个类的字面意思就能够解释一切:自动reset的事件,就是这个事件一旦set之后,如果某个线程堵塞被继续了,那么就会自动reset。下一次如果尝试继续,依然会被堵塞。

      其中AutoResetEvent类的构造函数有一个参数 是bool型。

     MSDN的解释是:

      Initializes a new instance of the AutoResetEvent class with a Boolean value indicating whether to set the initial state to signaled.

    如果这个参数是true,那么第一次尝试继续就不会被阻塞。如果这个参数是false,那么第一次尝试继续就会被堵塞。

    以下是测试代码,取自MSDN:


using System;
using System.Threading;

// Visual Studio: Replace the default class in a Console project with
//                the following class.
class Example
{
   private static AutoResetEvent event_1 = new AutoResetEvent(true);
   private static AutoResetEvent event_2 = new AutoResetEvent(false);

static void Main()
   {
       Console.WriteLine("Press Enter to create three threads and start them.\r\n" +
                         "The threads wait on AutoResetEvent #1, which was created\r\n" +
                         "in the signaled state, so the first thread is released.\r\n" +
                         "This puts AutoResetEvent #1 into the unsignaled state.");
       Console.ReadLine();

for (int i = 1; i < 4; i++)
       {
           Thread t = new Thread(ThreadProc);
           t.Name = "Thread_" + i;
           t.Start();
       }
       Thread.Sleep(250);

for (int i = 0; i < 2; i++)
       {
           Console.WriteLine("Press Enter to release another thread.");
           Console.ReadLine();
           event_1.Set();
           Thread.Sleep(250);
       }

Console.WriteLine("\r\nAll threads are now waiting on AutoResetEvent #2.");
       for (int i = 0; i < 3; i++)
       {
           Console.WriteLine("Press Enter to release a thread.");
           Console.ReadLine();
           event_2.Set();
           Thread.Sleep(250);
       }

// Visual Studio: Uncomment the following line.
       //Console.Readline();
   }

static void ThreadProc()
   {
       string name = Thread.CurrentThread.Name;

Console.WriteLine("{0} waits on AutoResetEvent #1.", name);
       event_1.WaitOne();
       Console.WriteLine("{0} is released from AutoResetEvent #1.", name);

Console.WriteLine("{0} waits on AutoResetEvent #2.", name);
       event_2.WaitOne();
       Console.WriteLine("{0} is released from AutoResetEvent #2.", name);

Console.WriteLine("{0} ends.", name);
   }
}

其中,AutoResetEvent.WaitOne()这个方法就是线程中尝试继续。如果没有SET信号,那么就会一直阻塞,如果收到Set信号该线程就会继续。但是因为是AutoResetEvent,所以下一次waitOne依然会被阻塞。

上面代码的输出结果是:


Press Enter to create three threads and start them.
The threads wait on AutoResetEvent #1, which was created
in the signaled state, so the first thread is released.
This puts AutoResetEvent #1 into the unsignaled state.

Thread_1 waits on AutoResetEvent #1.
Thread_1 is released from AutoResetEvent #1.
Thread_1 waits on AutoResetEvent #2.
Thread_3 waits on AutoResetEvent #1.
Thread_2 waits on AutoResetEvent #1.
Press Enter to release another thread.

Thread_3 is released from AutoResetEvent #1.
Thread_3 waits on AutoResetEvent #2.
Press Enter to release another thread.

Thread_2 is released from AutoResetEvent #1.
Thread_2 waits on AutoResetEvent #2.

All threads are now waiting on AutoResetEvent #2.
Press Enter to release a thread.

Thread_2 is released from AutoResetEvent #2.
Thread_2 ends.
Press Enter to release a thread.

Thread_1 is released from AutoResetEvent #2.
Thread_1 ends.
Press Enter to release a thread.

Thread_3 is released from AutoResetEvent #2.
Thread_3 ends.

二,ManualResetEvent

ManualResetEvent和AutoResetEvent大部分概念都是相同的,最大的不同就是一个是自动reset一个是手动reset。也就是说,如果使用ManualResetEvent类,一旦Set之后,所有已经阻塞的线程(waitone())都会继续。而且之后调用waitone的线程也不会被堵塞,除非手动再次Reset。也就是说,这个类是手动开启关闭信号的事件。

以下是测试代码,取自MSDN:


using System;
using System.Threading;

public class Example
{
   // mre is used to block and release threads manually. It is
   // created in the unsignaled state.
   private static ManualResetEvent mre = new ManualResetEvent(false);

static void Main()
   {
       Console.WriteLine("\nStart 3 named threads that block on a ManualResetEvent:\n");

for(int i = 0; i <= 2; i++)
       {
           Thread t = new Thread(ThreadProc);
           t.Name = "Thread_" + i;
           t.Start();
       }

Thread.Sleep(500);
       Console.WriteLine("\nWhen all three threads have started, press Enter to call Set()" +
                         "\nto release all the threads.\n");
       Console.ReadLine();

mre.Set();

Thread.Sleep(500);
       Console.WriteLine("\nWhen a ManualResetEvent is signaled, threads that call WaitOne()" +
                         "\ndo not block. Press Enter to show this.\n");
       Console.ReadLine();

for(int i = 3; i <= 4; i++)
       {
           Thread t = new Thread(ThreadProc);
           t.Name = "Thread_" + i;
           t.Start();
       }

Thread.Sleep(500);
       Console.WriteLine("\nPress Enter to call Reset(), so that threads once again block" +
                         "\nwhen they call WaitOne().\n");
       Console.ReadLine();

mre.Reset();

// Start a thread that waits on the ManualResetEvent.
       Thread t5 = new Thread(ThreadProc);
       t5.Name = "Thread_5";
       t5.Start();

Thread.Sleep(500);
       Console.WriteLine("\nPress Enter to call Set() and conclude the demo.");
       Console.ReadLine();

mre.Set();

// If you run this example in Visual Studio, uncomment the following line:
       //Console.ReadLine();
   }

private static void ThreadProc()
   {
       string name = Thread.CurrentThread.Name;

Console.WriteLine(name + " starts and calls mre.WaitOne()");

mre.WaitOne();

Console.WriteLine(name + " ends.");
   }
}

输出结果是:


Start 3 named threads that block on a ManualResetEvent:

Thread_0 starts and calls mre.WaitOne()
Thread_1 starts and calls mre.WaitOne()
Thread_2 starts and calls mre.WaitOne()

When all three threads have started, press Enter to call Set()
to release all the threads.

Thread_2 ends.
Thread_0 ends.
Thread_1 ends.

When a ManualResetEvent is signaled, threads that call WaitOne()
do not block. Press Enter to show this.

Thread_3 starts and calls mre.WaitOne()
Thread_3 ends.
Thread_4 starts and calls mre.WaitOne()
Thread_4 ends.

Press Enter to call Reset(), so that threads once again block
when they call WaitOne().

Thread_5 starts and calls mre.WaitOne()

Press Enter to call Set() and conclude the demo.

Thread_5 ends.

ManualResetEvent类的输出结果与AutoResetEvent输出结果最大的不同是在于:

如果不手动Reset,一旦调用Set方法,那么ManualResetEvent.WaitOne()就不会堵塞。

但是,AutoResetEvent会自动Reset,所以哪怕不手动Reset,每一次AutoResetEvent.WaitOne()都需要Set方法进行触发以继续线程。

来源:https://www.cnblogs.com/mq0036/p/14202385.html

标签:c#,线程
0
投稿

猜你喜欢

  • C++实现企业职工工资管理系统

    2023-11-02 17:34:34
  • Java全面细致讲解==和equals的使用

    2021-08-20 05:28:43
  • Android实现在xml文件中引用自定义View的方法分析

    2021-05-28 01:43:57
  • Android悬浮对话框(即点即关对话框)实现代码

    2023-01-01 04:53:54
  • Java中stream处理中map与flatMap的比较和使用案例

    2023-11-21 02:27:53
  • C++调试追踪class成员变量的方法

    2022-11-21 01:32:12
  • Java8 使用工厂方法supplyAsync创建CompletableFuture实例

    2023-02-14 03:57:22
  • SpringBoot+netty-socketio实现服务器端消息推送

    2023-11-15 06:14:31
  • WinForm实现为ComboBox绑定数据源并提供下拉提示功能

    2021-12-16 11:00:38
  • 通过图例了解PowerDesigner使用方法

    2021-06-05 23:16:24
  • SpringBoot整合Redis将对象写入redis的实现

    2023-07-30 14:39:07
  • java之swing表格实现方法

    2023-06-05 09:44:03
  • 如何用Java注解和反射实现依赖注入

    2022-11-04 11:34:10
  • JAVA中阻止类的继承(官方和非官方)

    2023-06-29 16:02:56
  • Java生成随机姓名、性别和年龄的实现示例

    2023-11-13 16:43:26
  • 配置Android SDK

    2023-12-05 09:57:29
  • 教你用JAVA写文本编辑器(一)

    2023-06-13 09:05:31
  • Java数据结构之线索化二叉树的实现

    2022-03-16 15:25:34
  • C#入门之定义类成员与接口实现

    2023-05-25 09:50:58
  • 基于Android AIDL进程间通信接口使用介绍

    2021-12-28 05:15:22
  • asp之家 软件编程 m.aspxhome.com