本文主要来自一道面试题,由于之前对AutoResetEvent的概念比较模糊(即使已经使用过了)。面试题题目很简洁:两个线程交替打印0~100的奇偶数。你可以先动手试试,我主要是尝试在一个方法里面完成这个任务。

注: Suspend,Resume来控制线程已经在.net framework2.0被淘汰了,原因就是挂起之后,但因为异常而没有及时恢复,如果占用资源会导致死锁。

AutoResetEvent概念

  • AutoResetEvent对象用来进行线程同步操作,AutoResetEvent类继承waitHandle类。waitOne()方法就继承来自waitHandle类。
  • AutoResetEvent对象有终止和非终止两种状态,终止状态是线程继续执行,非终止状态使线程阻塞,可以调用set和reset方法使对象进入终止和非终止状态。-》可以简单理解如果AutoResetEvent对象是终止状态,就像不管别人了,任你撒野去(waitOne()得到的都是撒野信号)
  • AutoResetEvent顾名思义,其对象在调用一次set之后会自动调用一次reset,进入非终止状态使调用了等待方法的线程进入阻塞状态。-》可以简单理解如果AutoResetEvent对象是非终止状态,就开始管理起别人来了,此时waitOne()得到的信号都是呆在原地不动信号。
  • waitHandle对象的waitone可以使当前线程进入阻塞状态,等待一个信号。直到当前 waitHandle对象收到信号,才会继续执行。
  • set可以发送一个信号,允许一个调用waitone而等待线程继续执行。 ManulResetEvent的set方法可以允许多个。但是要手动关闭,即调用reset();
  • reset可以使因为调用waitone() 而等待线程都进入阻塞状态。

AutoResetEvent主要方法及实践

  1. AutoResetEvent(bool initialState):构造函数,用一个指示是否将初始状态设置为终止的布尔值初始化该类的新实例。 false:无信号,子线程的WaitOne方法不会被自动调用 true:有信号,子线程的WaitOne方法会被自动调用
  2. Reset ():将事件状态设置为非终止状态,导致线程阻止;如果该操作成功,则返回true;否则,返回false。
  3. Set ():将事件状态设置为终止状态,允许一个或多个等待线程继续;如果该操作成功,则返回true;否则,返回false。
  4. WaitOne(): 阻止当前线程,直到收到信号。
  5. WaitOne(TimeSpan, Boolean) :阻止当前线程,直到当前实例收到信号,使用 TimeSpan 度量时间间隔并指定是否在等待之前退出同步域。

有了上面的解释,开始展示代码(经过多次优化)

 //若要将初始状态设置为终止,则为 true;若要将初始状态设置为非终止,则为 false
static AutoResetEvent oddResetEvent = new AutoResetEvent(false);
static AutoResetEvent evenResetEvent = new AutoResetEvent(false);
static int i = 0;
static void Main(string[] args)
{
//ThreadStart是个委托
Thread thread1 = new Thread(new ThreadStart(show));
thread1.Name = "偶数线程";
Thread thread2 = new Thread(new ThreadStart(show));
thread2.Name = "奇数线程";
thread1.Start();
Thread.Sleep(2); //保证偶数线程先运行。
thread2.Start();
Console.Read(); }
public static void show()
{
while (i <= 100)
{
int num = i % 2;
if (num == 0)
{
Console.WriteLine("{0}:{1} {2} ", Thread.CurrentThread.Name, i++, "evenResetEvent");
if(i!=1) evenResetEvent.Set();
oddResetEvent.WaitOne(); //当前线程阻塞 }
else
{
Console.WriteLine("{0}:{1} {2} ", Thread.CurrentThread.Name, i++, "oddResetEvent");
//如果此时AutoResetEvent 为非终止状态,则线程会被阻止,并等待当前控制资源的线程通过调用 Set 来通知资源可用。否则不会被阻止
oddResetEvent.Set();
evenResetEvent.WaitOne();
}
}
}

结果如下图所示:

注意点:

不要有一点点点点多余的evenResetEvent.Set(),他会让后续的 evenResetEvent.WaitOne();失效.

第二种方法Semaphore

此外,我们利用信号量也可以实现,信号量是一种内核模式锁,对性能要求比较高,特殊情况下才考虑使用,而且要避免在内核模式和用户模式下频繁相互切换线程。代码如下:

 private static readonly int MaxSize = 1;
private static int i = 0;
static Semaphore oddSemaphore = new Semaphore(0, MaxSize);
static Semaphore evenSemaphore = new Semaphore(0, MaxSize); static void Main(string[] args)
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
//ThreadStart是个委托
Thread thread1 = new Thread(new ThreadStart(show));
thread1.Name = "偶数线程";
Thread thread2 = new Thread(new ThreadStart(show));
thread2.Name = "奇数线程";
thread1.Start();
thread2.Start();
thread1.Join();
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds);
Console.Read();
} private static void show()
{
if(i==1) evenSemaphore.WaitOne();
while (i <= 100)
{
int num = i % 2;
if (num == 0)
{
Console.WriteLine("{0}:{1} {2} ", Thread.CurrentThread.Name, i++, Thread.CurrentThread.ManagedThreadId);
evenSemaphore.Release();
oddSemaphore.WaitOne(); //当前线程阻塞
}
else
{
Console.WriteLine("{0}:{1} {2} ", Thread.CurrentThread.Name, i++, Thread.CurrentThread.ManagedThreadId);
//释放一个偶数信号空位出来;
oddSemaphore.Release();
evenSemaphore.WaitOne(); //当前线程阻塞
//此时已经消耗了一个奇数信号空位
}
}
}

第三种方法,约定每个线程只干自己的事

这种方法利用线程池本身就是队列的方式,即先进先出。测试之后发现性能有下降,但是还是贴出来供参考。

      static int threadCount = 2;
static int count = 0;
static object cursorLock = new object();
static void Main(string[] args)
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
Task[] arr = new Task[2];
for (int threadIndex = 0; threadIndex < threadCount; threadIndex++)
{
//这两种方法都可以
arr[threadIndex] = Task.Factory.StartNew(PrintNum, threadIndex);
}
Task.WaitAll(arr);
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds);
Console.Read();
} private static void PrintNum(object num)
{
bool isOk = false;
while (!isOk)
{
lock (cursorLock)
{
int index = count % 2;
if (count>100)
{
isOk = true;
}
else if (index == (int)num)
{
if (index == 0) Console.WriteLine("{0}:{1} {2} ", "偶数线程", Thread.CurrentThread.ManagedThreadId, count++);
else Console.WriteLine("{0}:{1} {2} ", "奇数线程", Thread.CurrentThread.ManagedThreadId, count++);
}
}
}
}

结果如下:

第四种方法 Mutex

        private static int i = 0;
static Mutex mutex = new Mutex(); static void Main(string[] args)
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
//ThreadStart是个委托
Thread thread1 = new Thread(new ParameterizedThreadStart(show));
thread1.Name = "偶数线程";
Thread thread2 = new Thread(new ParameterizedThreadStart(show));
thread2.Name = "奇数线程";
thread1.Start(0);
thread2.Start(1);
thread2.Join();
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds);
Console.Read();
}
/// <summary>
/// Mutex的释放与锁定 都只能在同一个线程中执行
/// </summary>
private static void show(object index)
{
while (i <= 100)
{
mutex.WaitOne();
int num = i % 2;
if (num == (int)index&&i<=100)
{
Console.WriteLine("{0}:{1} {2} ", Thread.CurrentThread.Name, i++, Thread.CurrentThread.ManagedThreadId);
}
mutex.ReleaseMutex();
} }

有关概念资料

https://www.cnblogs.com/michaelxu/archive/2008/09/20/1293716.html

AutoResetEvent控制线程用法的更多相关文章

  1. C# 使用AutoResetEvent进行线程同步

    AutoResetEvent 允许线程通过发信号互相通信. 通常,当线程需要独占访问资源时使用该类. 线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号. 如果 AutoRe ...

  2. MFC多线程各种线程用法 .

    http://blog.csdn.net/qq61394323/article/details/9328301 一.问题的提出 编写一个耗时的单线程程序: 新建一个基于对话框的应用程序SingleTh ...

  3. C# 多线程 线程池(ThreadPool) 2 如何控制线程池?

    线程池启动了,但是没有方法去控制线程池,如果子线程出现了问题,难道线程池就死了吗? 我们可以设置线程池的线程数量,进行加入任务,线程池会自动分配并且合理的执行,但是控制不了又有啥意思呢. 线程池里线程 ...

  4. Condition控制线程通信

    Condition控制线程通信 一.前言 java.util.concurrent.locks.Condition 接口描述了可能会与锁有关联的条件变量.这些变量在用法上与使用Object.wait ...

  5. Java---Condition控制线程通信

    java中控制线程通信的方法有:1.传统的方式:利用synchronized关键字来保证同步,结合wait(),notify(),notifyAll()控制线程通信.不灵活. 2.利用Conditio ...

  6. Java核心知识点学习----使用Condition控制线程通信

    一.需求 实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次. 即:A->B->C---A->B->C---A-> ...

  7. 使用Thread类可以创建和控制线程

    1.创建线程 static void Main(string[] args) { /* Thread类 * 创建控制线程 * 其构造函数接受ThreadStart和ParameterizedThrea ...

  8. [转载]C# 多线程、控制线程数提高循环输出效率

    C#多线程及控制线程数量,对for循环输出效率. 虽然输出不规律,但是效率明显提高. 思路: 如果要删除1000条数据,只使用for循环,则一个接着一个输出.所以,把1000条数据分成seed段,每段 ...

  9. Java中怎么控制线程訪问资源的数量

    在API中是这样来描写叙述Semaphore 的 Semaphore 通经常使用于限制能够訪问某些资源(物理或逻辑的)的线程数目. 一个计数信号量.从概念上讲,信号量维护了一个许可集.如有必要,在许可 ...

随机推荐

  1. TigerGraph入门

    测试机器配置 1G内存,1个核,CentOS Linux release 7.4.1708 (Core)的云主机,一块50G HDD的云主机. 1. 安装 下载了目前最新的开发者版本,下载链接:htt ...

  2. 数据库触发器_trigger

    部门表_删除: USE [test] GO /****** Object: Trigger [dbo].[部门_Delete] Script Date: 2015/8/31 16:41:46 **** ...

  3. [原创]Floodlight安装

    Floodlight安装:一.安装环境: ubuntu-12.04-64bit二.安装Floodlight: #apt-get update #apt-get install build-essent ...

  4. 微信小程序 setData 数组 渲染问题 删除之后的数组渲染不正确

    list: [ { id: , mode: , src: ' }, { id: , mode: , src: ' }, { id: , mode: , src: ' } ], onDelete(e) ...

  5. 理解 Spring 定时任务的 fixedRate 和 fixedDelay 的区别

    用过 Spring 的 @EnableScheduling 的都知道,有三种方式,即 @Scheduled 注解的 fixedRate(fixedRateString), fixedDelay(fix ...

  6. c语言进阶12-线性表之顺序表

    一.  线性表的定义 为什么要学习线性表呢? 因为我们日常生活中存在种数据关系,计算机程序是为了解决日常生活的数据关系,因此我们要学习线性表. 线性表是什么呢? 线性表是由n个元素组成的有限序列. 需 ...

  7. Java 多线程部分面试题

    1..什么是线程,什么是进程,它们有什么区别和联系,一个进程里面是否必须有个线程 进程和线程的主要差别在于它们是不同的操作系统资源管理方式.进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它 ...

  8. SQL server 2014使用导出数据为Excel

    1.打开SQL server 2014,连接至数据库引擎 2.在要导出的数据库上右击,选择"任务->导出数据" 3.数据源选择"SQL Server Native ...

  9. C 单链表 实现约瑟夫环

    list.h #ifndef _List_H #define _List_H typedef int ElementType; struct Node; typedef struct Node *Pt ...

  10. zstack源码编译安装(1.7.x版本)

    图片没粘贴过来,请看本人gitbook吧https://www.gitbook.com/book/jingtyu/how-to-learn-zstack-code 运行环境 zstack的安装方式有很 ...