最近捣鼓了一下多线程的同步问题,发现其实C#关于多线程同步事件处理还是很灵活,这里主要写一下,自己测试的一些代码,涉及到了AutoResetEvent 和 ManualResetEvent,当然还有也简要提了一下System.Threading.WaitHandle.WaitOne 、System.Threading.WaitHandle.WaitAny和System.Threading.WaitHandle.WaitAll ,下面我们一最初学者的角度来看,多线程之间的同步。

假设有这样的一个场景,主线程开了一个子线程,让子线程等着,等主线程完成了某件事情时再通知子线程去往下执行,这里关键就在于这个怎让子线程等着,主线程怎通知子线程,一般情况下我们不难想到用一个公共变量,于是咱们就有了下面的代码:

[csharp] view plaincopy
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;  

namespace AutoResetEventTest
{
    class Class1
    {
        static bool flag = true;  

        static void DoWork()
        {
            Console.WriteLine("   worker thread started, now waiting on event...");
            while (flag)
            {  

            }
            Console.WriteLine("   worker thread reactivated, now exiting...");
        }  

        static void Main()
        {
            Console.WriteLine("main thread starting worker thread...");
            Thread t = new Thread(DoWork);
            t.Start();  

            Console.WriteLine("main thrad sleeping for 1 second...");
            Thread.Sleep();  

            Console.WriteLine("main thread signaling worker thread...");
            flag = false;
        }
    }
}
虽然目的达到了,但是看着这代码就纠结,下面该是我们的主角上场了,AutoResetEvent 和 ManualResetEvent,关于这两者我们暂且认为是差不多了,稍后我会介绍他们的不同,这里以AutoResetEvent为例,其实很多官方的说法太过于抽象,这里通俗地讲,可以认为AutoResetEvent就是一个公共的变量(尽管它是一个事件),创建的时候可以设置为false,然后在要等待的线程使用它的WaitOne方法,那么线程就一直会处于等待状态,只有这个AutoResetEvent被别的线程使用了Set方法,也就是要发通知的线程使用了它的Set方法,那么等待的线程就会往下执行了,Set就是发信号,WaitOne是等待信号,只有发了信号,等待的才会执行。如果不发的话,WaitOne后面的程序就永远不会执行。好下面看用AutoResetEvent改造上面的程序:

[csharp] view plaincopy
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;  

namespace AutoResetEventTest
{
    class Class2
    {
        static AutoResetEvent mEvent=new AutoResetEvent(false);
        //static ManualResetEvent mEvent = new ManualResetEvent(false);  

        static void DoWork()
        {
            Console.WriteLine("   worker thread started, now waiting on event...");
            mEvent.WaitOne();
            Console.WriteLine("   worker thread reactivated, now exiting...");
        }  

        static void Main()
        {
            Console.WriteLine("main thread starting worker thread...");
            Thread t = new Thread(DoWork);
            t.Start();  

            Console.WriteLine("main thrad sleeping for 1 second...");
            Thread.Sleep();  

            Console.WriteLine("main thread signaling worker thread...");
            mEvent.Set();
        }
    }
}
这时代码是不是清爽多了,这里其实你还会看到,把上面的AutoResetEvent换成ManualResetEvent也是没有问题的,那么它两之间的区别是什么呢?个人认为它们最大的区别在于,无论何时,只要 AutoResetEvent 激活线程,它的状态将自动从终止变为非终止。相反,ManualResetEvent 允许它的终止状态激活任意多个线程,只有当它的 Reset 方法被调用时才还原到非终止状态。开下面的代码:

[csharp] view plaincopy
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;  

namespace AutoResetEventTest
{
    class Class3
    {
        static AutoResetEvent mEvent = new AutoResetEvent(false);
        //static ManualResetEvent mEvent = new ManualResetEvent(false);  

        static void DoWork()
        {
            Console.WriteLine("   worker thread started, now waiting on event...");
            ; i < ; i++)
            {
                mEvent.WaitOne();
                //mEvent.Reset();
                Console.WriteLine("   worker thread reactivated, now exiting...");
            }
        }  

        static void Main()
        {
            Console.WriteLine("main thread starting worker thread...");
            Thread t = new Thread(DoWork);
            t.Start();  

            ; i < ; i++)
            {
                Thread.Sleep();
                Console.WriteLine("main thread signaling worker thread...");
                mEvent.Set();
            }
        }
    }
}
如果你想仅仅把AutoResetEvent换成ManualResetEvent的话,你发现输出就会乱套了,为什么呢?
假如有autoevent.WaitOne()和manualevent.WaitOne(),当线程得到信号后都得以继续执行。差别就在调用后,autoevent.WaitOne()每次只允许一个线程进入,当某个线程得到信号(也就是有其他线程调用了autoevent.Set()方法后)后,autoevent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待,也就是说,autoevent一次只唤醒一个线程。而manualevent则可以唤醒多个线程,当某个线程调用了set方法后,其他调用waitone的线程获得信号得以继续执行,而manualevent不会自动将信号置为不发送,也就是说,除非手工调用了manualevent.Reset()方法,否则manualevent将一直保持有信号状态,manualevent也就可以同时唤醒多个线程继续执行。

在上面代码中,如果将AutoResetEvent换成ManualResetEvent的话,只要要在waitone后面做下reset,就会达到同样的效果。

之后咱们再来个简单的例子:

[csharp] view plaincopy
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;  

namespace AutoResetEventTest
{
    class Class4
    {
        public static AutoResetEvent mEvent = new AutoResetEvent(false);  

        public static void trmain()
        {
            Thread tr = Thread.CurrentThread;
            Console.WriteLine("thread: waiting for an event");
            mEvent.WaitOne();
            Console.WriteLine("thread: got an event");
            ; x < ; x++)
            {
                Thread.Sleep();
                Console.WriteLine(tr.Name + ": " + x);
            }
        }
        static void Main(string[] args)
        {
            Thread thrd1 = new Thread(new ThreadStart(trmain));
            thrd1.Name = "thread1";
            thrd1.Start();
            ; x < ; x++)
            {
                Thread.Sleep();
                Console.WriteLine("Main:" + x);
                 == x) mEvent.Set();
            }
            while (thrd1.IsAlive)
            {
                Thread.Sleep();
                Console.WriteLine("Main: waiting for thread to stop");
            }
        }
    }
}
是不是更有感觉了?之后咱来看看另外几个东东:

System.Threading.WaitHandle.WaitOne 使线程一直等待,直到单个事件变为终止状态;

System.Threading.WaitHandle.WaitAny 阻止线程,直到一个或多个指示的事件变为终止状态;

System.Threading.WaitHandle.WaitAll 阻止线程,直到所有指示的事件都变为终止状态。

然后再来个例子,以WaitAll使用为例:

[csharp] view plaincopy
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;  

namespace AutoResetEventTest
{
    class other
    {
        static void Main(string[] args)
        {
            Random randomGenerator = new Random();
            AutoResetEvent[] resets=];  

            ; i < ; i++)
            {
                resets[i] = new AutoResetEvent(false);
                )+;  

                worker w = new worker(wTime, resets[i]);  

                Thread thrd1 = new Thread(new ThreadStart(w.work));
                thrd1.Start();
            }
            WaitHandle.WaitAll(resets);
            Console.WriteLine("ALL worker done - main exiting.");
        }  

    }  

    public class worker
    {
        public string name;
        public int wTime;
        public AutoResetEvent mEvent;  

        public worker(int w, AutoResetEvent m)
        {
            name = w.ToString();
            wTime = w * ;
            mEvent = m;
        }  

        public void work()
        {
            Console.WriteLine(name + " worker thread waiting for " + wTime + "....");
            Thread.Sleep(wTime);
            Console.WriteLine(name + " worker thread back...");
            mEvent.Set();
        }
    }
}
简单来说就是,开了5个线程,每个线程随机休眠若干秒,都完成后通知主线程退出,这里就开了一个AutoResetEvent数组,主线程就WaitHandle.WaitAll(resets) ,子线程休眠完后就Set1个AutoResetEvent,最后都Set完后,主线程就会往下执行。最后最后再来个买书付款取货的例子,加深理解:

[csharp] view plaincopy
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;  

namespace AutoResetEventTest
{
    class Program
    {
        ;
        static AutoResetEvent myResetEvent = new AutoResetEvent(false);
        static AutoResetEvent ChangeEvent = new AutoResetEvent(false);
        //static ManualResetEvent myResetEvent = new ManualResetEvent(false);
        //static ManualResetEvent ChangeEvent = new ManualResetEvent(false);
        static int number; //这是关键资源  

        static void Main()
        {
            Thread payMoneyThread = new Thread(new ThreadStart(PayMoneyProc));
            payMoneyThread.Name = "付钱线程";
            Thread getBookThread = new Thread(new ThreadStart(GetBookProc));
            getBookThread.Name = "取书线程";
            payMoneyThread.Start();
            getBookThread.Start();  

            ; i <= numIterations; i++)
            {
                Console.WriteLine("买书线程:数量{0}", i);
                number = i;
                //Signal that a value has been written.
                myResetEvent.Set();
                //ChangeEvent.Set();
                Thread.Sleep();
            }
            payMoneyThread.Abort();
            getBookThread.Abort();
        }  

        static void PayMoneyProc()
        {
            while (true)
            {
                myResetEvent.WaitOne();
                //myResetEvent.Reset();
                Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);
                ChangeEvent.Set();
            }
        }
        static void GetBookProc()
        {
            while (true)
            {
                ChangeEvent.WaitOne();
                //ChangeEvent.Reset();
                Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);
                Console.WriteLine("------------------------------------------");
                //Thread.Sleep(0);
            }
        }
    }
}  

C#多线程同步事件及等待句柄AutoResetEvent 和 ManualResetEvent的更多相关文章

  1. C# 多线程(lock,Monitor,Mutex,同步事件和等待句柄)

    本篇从 Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler 的类关系图开始,希望通过本篇的介绍能对常见的线程同步方法有一个整体的认识,而 ...

  2. C#多线程:深入了解线程同步lock,Monitor,Mutex,同步事件和等待句柄(中)

    本篇继续介绍WaitHandler类及其子类 Mutex,ManualResetEvent,AutoResetEvent的用法..NET中线程同步的方式多的让人看了眼花缭乱,究竟该怎么去理解呢?其实, ...

  3. 【转】多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上)

    本篇从Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler的类关系图开始,希望通过 本篇的介绍能对常见的线程同步方法有一个整体的认识,而对 ...

  4. windows多线程同步--事件

    推荐参考博客:秒杀多线程第六篇 经典线程同步 事件Event   事件是内核对象,多用于线程间通信,可以跨进程同步 事件主要用到三个函数:CreateEvent,OpenEvent,SetEvent, ...

  5. [Java][Android] 多线程同步-主线程等待全部子线程完毕案例

    有时候我们会遇到这种问题:做一个大的事情能够被分解为做一系列相似的小的事情,而小的事情无非就是參数上有可能不同样而已! 此时,假设不使用线程,我们势必会浪费许多的时间来完毕整个大的事情.而使用线程的话 ...

  6. 多线程间通信之AutoResetEvent和ManualResetEvent的原理分析和开发示例

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

  7. C#中的多线程 - 同步基础

    原文:http://www.albahari.com/threading/part2.aspx 文章来源:http://blog.gkarch.com/threading/part2.html 1同步 ...

  8. C#中的多线程 - 同步基础 z

    原文:http://www.albahari.com/threading/part2.aspx 专题:C#中的多线程 1同步概要Permalink 在第 1 部分:基础知识中,我们描述了如何在线程上启 ...

  9. 总结windows多线程同步互斥

    windows多线程同步互斥--总结 我的windows多线程系列文章: windows多线程--原子操作 windows多线程同步--事件 windows多线程同步--互斥量 windows多线程同 ...

随机推荐

  1. How to Operate SharePoint User Alerts with PowerShell

    When you migrate list or site, the user alerts in the site will not be migrated together with the co ...

  2. HTML5快速入门(三)—— 标签综合运用

    前言: 1.HTML5的发展非常迅速,可以说已经是前端开发人员的标配,在电商类型的APP中更是运用广泛,这个系列的文章是本人自己整理,尽量将开发中不常用到的剔除,将经常使用的拿出来,使需要的朋友能够真 ...

  3. 【android 开 发 】 - Android studio 下 NDK Jni 开发 简单例子

    Android 开发了一段时间,一方面 ,感觉不留下点什么.有点对不起自己, 另一方面,好记性不如烂笔头,为了往后可以回头来看看,就当做是笔记,便决定开始写博客.废话不多说 ! 今天想搞一搞 ndk ...

  4. 《javascript权威指南》读书笔记——第一篇

    <javascript权威指南>读书笔记——第一篇 金刚 javascript js javascript权威指南 由于最近想系统学习下javascript,所以开始在kindle上看这本 ...

  5. 【转】2016/2017 Web 开发者路线图

    链接:知乎 [点击查看大图] 原图来自LearnCodeAcademy最火的视频,learncode是YouTube上最火的Web开发教学频道,介绍包括HTML/CSS/JavaScript/Subl ...

  6. Play Framework 完整实现一个APP(五)

    程序以及基本可用了,需要继续完善页面 1.创建页面模板 创建文件 app/views/tags/display.html *{ Display a post in one of these modes ...

  7. Java中GC的工作原理

    转文: 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能提高整个 ...

  8. [Oracle]快速插入大量(100w)数据

    背景:无论在开发调试或者软件测试中,测试数据的准备是调试/测试执行前重要和必要的一个环节,因此以下几种方式可以快速插入大量数据: 第一种方法: declare   -- Local variables ...

  9. [Erlang 0105] Erlang Resources 小站 2013年1月~6月资讯合集

    很多事情要做,一件一件来; Erlang Resources 小站 2013年1月~6月资讯合集,方便检索.      小站地址: http://site.douban.com/204209/     ...

  10. ORA-02266: unique/primary keys in table referenced by enabled foreign keys

    在数据库里面使用TRUNCATE命令截断一个表的数据时,遇到如下错误 SQL >TRUNCATE TABLE ESCMOWNER.SUBX_ITEM ORA-02266: unique/prim ...