服务总结 -多线程 - 线程同步(AutoResetEvent与ManualResetEvent)

前言

在我们编写多线程程序时,会遇到这样一个问题:在一个线程处理的过程中,需要等待另一个线程处理的结果才能继续往下执行。比如:有两个线程,一个用来接收Socket数据,另一个用来处理Socket数据,而处理Socket数据的那个线程需要在接收到Socket数据后才能处理运行,就要等待接收线程接收数据。那么处理线程如何等待,接收线程又如何通知处理线程呢?

其中一个比较好的方式就是使用AutoResetEvent/ManualResetEvent

1. AutoResetEvent/ManualResetEvent介绍

AutoResetEvent/ManualResetEvent是.Net给我们提供的用于线程间同步的对象。都有三个主要的函数:

WaitOne: 用于阻塞线程,等待接收到继续运行信号

Set:          用于发送同步信号,通知正在等待的线程继续运行

Reset:      重置终止状态

初始化构造函数ResetEvent(bool initialState),当initialState为true时,默认为终止状态(即阻塞无效);当initialState为false时,默认为非终止状态

关于ResetEvent的终止状态与非终止状态,大家刚开始看的时候会感到难以理解,我举个很形象的例子,大家就会明白了。

我们都有上班刷卡进大楼的经历,这里可以把ResetEvent看作是那个闸门,只有刷卡才能进。

WaitOne就是等待刷卡,Set就是刷卡,而Reset则是关闭闸门,这样就知道,当初始化的时候,initialState为true时,意思就是说,默认闸门是开的;initialState为false时,默认闸门是关的

所以,如果要进入大楼,步骤就应该是这样的(假设当前闸门是关着的),

等待刷卡(WaitOne) ->刷卡(Set)->通信->关闭闸门(Reset)

2. AutoResetEvent/ManualResetEvent的区别

其实,这两个类的字面上的意思已经告诉了我们他们间的区别,Reset既然是关闭闸门,那也就是说,Auto是自动关闭闸门,Manual是手动关闭闸门。

AutoResetEvent:刷卡通过后,闸门自动关闭,然后等待下一次刷卡

ManualResetEvent:刷卡通过后,闸门不会自动关闭,如果不手动关闭(调用Reset方法),等待刷卡无效,人也就不用刷卡就能直接通过

所以WaitOne是否有效,能不能阻塞线程取决于是否调用了Reset方法。

而构造函数中initialState为true时,表示闸门默认第一次是打开的

3. AutoResetEvent/ManualResetEvent使用示例

接下来,我们用一个简单的示例来看一下这两个类的实际效果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ResetEventTest
{
    class Program
    {     //初始化时默认为非终止状态false
        static AutoResetEvent autoRE = new AutoResetEvent(false);
        static ManualResetEvent manualRE = new ManualResetEvent(false);

        static void Main(string[] args)
        {
            (new Thread(AutoMethod1)).Start();
            (new Thread(AutoMethod2)).Start();

            (new Thread(ManualMethod1)).Start();
            (new Thread(ManualMethod2)).Start();

            Console.ReadKey();
        }

        static void AutoMethod1()
        {
            Console.WriteLine("wait print AutoMethod 1:");
            autoRE.WaitOne();
            Console.WriteLine("AutoMethod 1");
        }

        static void AutoMethod2()
        {
            Console.WriteLine("wait print AutoMethod 2:");
            autoRE.WaitOne();
            Console.WriteLine("AutoMethod 2");
        }

        static void ManualMethod1()
        {
            Console.WriteLine("wait print ManualMethod 1:");
            manualRE.WaitOne();
            Console.WriteLine("ManualMethod 1");
        }

        static void ManualMethod2()
        {
            Console.WriteLine("wait print ManualMethod 2:");
            manualRE.WaitOne();
            Console.WriteLine("ManualMethod 2");
        }
    }
}

分别对应AutoResetEvent和ManualResetEvent启动两个线程,全部进入等待,这时候输出为:

我们发现线程全部阻塞,这说明初始化时false,就是设置为阻塞状态,那我们把初始化参数改为true试下

static AutoResetEvent autoRE = new AutoResetEvent(true);
static ManualResetEvent manualRE = new ManualResetEvent(true);

结果如下:

发现(注意输出顺序可能不同),Auto仅阻塞了一个,而Manual则两个都没有阻塞,全部输出了,实际上即是Auto放行一个后,立即自动调用了Reset方法,导致AutoMethod2在没有获取Set通行信号前无法继续运行;而Manual因为没有手动调用Reset方法,WaitOne形同虚设没有起任何作用。

还使用之前的代码,在启动线程的时候添加Set代码,如下:

static AutoResetEvent autoRE = new AutoResetEvent(false);
static ManualResetEvent manualRE = new ManualResetEvent(false);

static void Main(string[] args)
{
    (new Thread(AutoMethod1)).Start();
    autoRE.Set();
    (new Thread(AutoMethod2)).Start();

    (new Thread(ManualMethod1)).Start();
    manualRE.Set();
    (new Thread(ManualMethod2)).Start();

    Console.ReadKey();
}

当AutoMethod1线程启动后,调用了Set方法,那么AutoMethod1线程就获得了继续运行的信号,这时候启动AutoMethod2线程,因为AutoMethod1线程运行后自动重置了状态,所以AutoMethod2就会阻塞;而ManualMethod1线程之后调用Set,因为没有手动调用Reset重置状态,那么ManualMethod2将不会阻塞,如下结果验证了我们的想法:

?

如果想要ManualMethod2阻塞,那么只要在ManualMethod2中,获取到Set信号后,再调用Reset方法即可

static void ManualMethod1()
{
    Console.WriteLine("wait print ManualMethod 1:");
    manualRE.WaitOne();
    manualRE.Reset();
    Console.WriteLine("ManualMethod 1");
}

结果:

4. 其他操作

其实我们查看ResetEvent的继承关系会发现,实际上ResetEvent继承自抽象类WaitHandle,WaitHandle下的SignalAndWait,WaitAll与WaitAny方法提供了我们更多样的控制线程同步的方式,这个在以后会进一步探讨。

时间: 2024-08-08 01:27:22

服务总结 -多线程 - 线程同步(AutoResetEvent与ManualResetEvent)的相关文章

线程同步 –AutoResetEvent和ManualResetEvent

上一篇介绍了通过lock关键字和Monitor类型进行线程同步,本篇中就介绍一下通过同步句柄进行线程同步. 在Windows系统中,可以使用内核对象进行线程同步,内核对象由系统创建并维护.内核对象为内核所拥有,所以不同进程可以访问同一个内核对象, 如进程.线程.事件.信号量.互斥量等都是内核对象.其中,信号量,互斥体,事件是Windows专门用来进行线程同步的内核对象. 在.NET中,有一个WaitHandle抽象类,这个类型封装了一个Windows内核对象句柄,在C#代码中,我们就可以使用Wa

多线程&线程同步

线程 程序执行过程中,并发执行的代码段. 线程之间可以共享内存. 线程安全 增加了同步处理,确保在同一时刻,只有一个线程执行同步代码. 保证线程安全的方法就是锁机制 java中的任何对象都可以作为锁对象 synchronized(lock){....} 代码块中的代码被确保同一时间只有一个线程才能执行 同步方法是用当前对象作为同步对象(this) public synchronized int getTicket(){...} synchronized关键字也能加在方法上 确保同一时间只有一个线

黑马程序员--Java基础--多线程|线程同步

--Java培训.Android培训.iOS培训..Net培训 期待与您共同交流!-- 多线程基础.线程同步 1. 多线程基础 1.1. 进程和线程 1.1.1. 什么是进程 所谓进程(process)就是一块包含了某些资源的内存区域.操作系统利用进程把它的工作划分为一些功能单元.进程中所包含的一个或多个执行单元称为线程(thread).进程还拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问.线程只能归属于一个进程并且它只能访问该进程所拥有的资源.当操作系统创建一个进程后,该进程会自动

多线程——线程同步,死锁

线程同步: 为什么需要同步 ①   线程同步是为了防止多个线程访问一个数据对象时,对数据造成破坏. ②   线程的同步是保证多线程安全访问竞争资源的一种手段. 同步和锁 ①   Java中每一个对象都有一个内置锁. ②   当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁:当程序运行到synchronized同步代码块时,自动获得锁定对象的锁. ③   获得一个对象的锁也称为获取锁.锁定对象.在对象上锁定或在对象上同步.当程序运

Java多线程 线程同步

如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么你需要使用同步,并且,读写线程都必须用相同的监视器锁同步.--Brain同步规则 synchronized 所有对象都自动含有单一的锁,当在调用一个对象的任意synchronized方法时,此对象将被加锁. 对于某个特定对象来说,所有的synchronized方法共享同一个锁.所以某个线程在访问对象的一个synchronized方法时,其他线程访问该对象的任何synchronized方法都

Java多线程——线程同步

在之前,已经学习到了线程的创建和状态控制,但是每个线程之间几乎都没有什么太大的联系.可是有的时候,可能存在多个线程多同一个数据进行操作,这样,可能就会引用各种奇怪的问题.现在就来学习多线程对数据访问的控制吧. 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问.   一.多线程引起的数据访问安全问题 下面看一个经典的问题,银行取钱的问题: 1).你有一张银行卡,里面有50

JAVA多线程线程同步问题

线程同步 在多线程的编程环境下,可能看着没有问题的代码在运行几千上万或者更多次后,出现了一些看着很奇怪的问题,出现这样的问题的原因就是可能会有两个或者更多个线程进入了同一块业务处理代码中导致了判断失效.为了解决这个问题,JAVA引入了同步监视器来解决这个问题.同步监视器的通用方法就是同步代码块,也就是给一块代码加了同步锁. package cn.test.hf; import java.math.BigDecimal; /** * 模拟取钱操作 */public class RunnableTe

9 C++ Boost 多线程,线程同步

线程的创建 boost_thread,boost_system 多线程的创建 线程的参数传递 线程的创建方式 线程的join 加入join,回收线程 线程中断 线程中断2, 线程组 boost 线程的死锁 boost 线程递归锁 线程互斥锁,线程同步 unique_lock 锁,离开作用域自动释放 unique_lock 锁 示例 2,可以显式的释放锁 boost 1次初始化 boost 条件变量 boost 线程锁,一个账户往另外一个账户转钱案例 boost upgrade_lock 知识背景

第18章 多线程----线程同步

Java提供了线程同步的机制来防止资源访问的冲突. 1.线程安全 实际开发中,使用多线程程序的情况很多,如银行排号系统.火车站售票系统等.这种多线程的程序通常会发生问题. 以火车站售票系统为例,在代码中判断当前票数是否大于0,如果大于0则执行将该票出售给乘客功能,但当两个线程同时访问这段代码时(假如这时只剩下一张票),第一个线程将票售出,与此同时第二个线程也已经执行完成判断是否有票的操作,并得出结论票数大于0,于是它也执行售出操作,这样就会产生负数.所以在编写多线程程序时,应该考虑到线程安全问题