本文主要来自一道面试题,由于之前对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. 找到linux中当前java的安装位置

    先看java -version $java version "1.8.0_111" Java(TM) SE Runtime Environment (build 1.8.0_111 ...

  2. JWT(JSON WEB TOKEN)实例

    JWT的工具类 加密解密工具 package top.wintp.crud.util; import com.auth0.jwt.JWTSigner; import com.auth0.jwt.JWT ...

  3. AndroidStudio使用genymotion模拟器

    安装Genymotion之前首先要安装好virtualbox这个软件 virtual官方网站:https://www.virtualbox.org/ genymotion的官方网站: https:// ...

  4. Java秒杀系统实战系列~构建SpringBoot多模块项目

    摘要:本篇博文是“Java秒杀系统实战系列文章”的第二篇,主要分享介绍如何采用IDEA,基于SpringBoot+SpringMVC+Mybatis+分布式中间件构建一个多模块的项目,即“秒杀系统”! ...

  5. 基于ng-zorro的ASP.NET ZERO前端实现之代码生成器

    上一篇介绍了集成ng-zorro的过程,本篇我们来看下如何用abp官方的生成器来生成前端代码. Abp官方提供了一个强大的代码生成器 ASP.NET Zero Power Tools,它的Visual ...

  6. CSRF_TOKEN

    目的是防御CSRF攻击. Token就是令牌,最大的特点就是随机性,不可预测. CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中, ...

  7. SD卡操作

    读写SD卡 Context类的openFileInput和openFileOutput方法都是针对应用程序的数据文件夹进行的文件操作,由于手机的ROM容量有限,因此这种操作有一定局限性. 手机的SD卡 ...

  8. JS原生隐士标签扩展

    最近项目开发中,开发了不少的接口,有一个接口是这样子的.先从A公司拿到数据后,存放到我们公司数据库里,然后需要将数据展示给客户,下面这个界面,后台要实时刷新,后台写了个定时器,2S刷一次从后台拼接好H ...

  9. TestNG独立运行的几种方法.md

    目录 通过main函数调用testng.xml文件,然后打成可执行jar包执行 1 假设我们已经写好了testng.xml,现在我们需要写一个主类和main函数用来调用testng.xml 2 把项目 ...

  10. istio使用教程

    kubernetes各版本离线安装包 安装 安装k8s 强势插播广告 三步安装,不多说 安装helm, 推荐生产环境用helm安装,可以调参 release地址 如我使用的2.9.1版本 yum in ...