多线程:线程同步之WaitHandle

一、引言

在前面的文章中,我们是使用“锁”的方式实现了线程间的通信,这种通信方式比较笨重。除了锁之外,.NET中还提供了一些线程间更自由通讯的工具,他们提供了通过“信号”进行通讯的机制,通俗的比喻为“开门”、“关门”:Set()开门、Reset()关门、WaitOne()等着。

二、WaitHandle

WaitHandle位于System.Threading命名空间下,是用来封装等待对共享资源进行独占访问的操作系统特定的对象。WaitHandle是一个抽象类,我们一般不直接使用,而是使用它的派生类:

  1. AutoResetEvent。
  2. EventWaitHandle。
  3. ManualResetEvent。
  4. Mutex。
  5. Semaphore。

1、AutoResetEvent

AutoResetEvent表示线程同步事件在一个等待线程释放后收到信号时自动重置。此类不能被继承。

是一个自动阻塞,WaitOne()方法阻塞程序执行,Set()方法释放信息。当释放后阻塞的代码继续执行。但下一次执行还需要等待信号。

通俗来说,WaitOne()是关门,Set()是开门。但开门之后,执行完又自动关门,还需要开门。可以知道超时时间:

// 设置超时时间为2秒,如果2秒后没有信号程序继续执行
are.WaitOne(2000);

看下面的一个例子:

using System;
using System.Threading;

namespace AutoResetEventDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 通过构造函数创建,默认是关门
            AutoResetEvent are = new AutoResetEvent(false);
            Thread t1 = new Thread(() => {
                while (true)
                {
                    Console.WriteLine("开始等着开门");
                    // 执行完WaitOne之后自动关门
                    are.WaitOne();
                    Console.WriteLine("又关门了");
                }
            });
            // 启动线程
            t1.Start();
            Console.WriteLine("按任意键开门");
            Console.ReadKey();
            //开门
            are.Set();

            Console.WriteLine("按任意键开门");
            Console.ReadKey();
            //开门
            are.Set();

            Console.WriteLine("按任意键开门");
            Console.ReadKey();
            //开门
            are.Set();

            Console.ReadKey();
        }
    }
}

程序输出结果:

举一个形象点的例子,AutoResetEvent相当于火车或者地铁的闸机口,过了一个以后自动关门。

2、ManualResetEvent

Manual表示手动的。看下面的代码:

using System;
using System.Threading;

namespace ManualResetEventDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 通过构造函数初始化
            // false表示“初始状态为关门”,设置为true则表示初始状态为开门
            ManualResetEvent mre = new ManualResetEvent(false);

            // 创建一个线程
            Thread t1 = new Thread(() =>
            {
                // 因为初始化的状态为关门
                Console.WriteLine("开始等着开门");
                // 调用等着开门的方法,只有开门以后才会执行下面的代码
                mre.WaitOne();
                // 开门之后才会执行这句代码
                Console.WriteLine("终于开门了");
            });

            // 启动线程
            t1.Start();

            Console.WriteLine("按任意键开门");
            Console.ReadLine();
            // 调用开门的方法
            mre.Set();

            Console.ReadKey();

        }
    }
}

运行程序,什么也不输入,看一下输出结果:

我们随意输入,再看运行结果:

怎么关门呢?关门也需要我们手动的调用:

using System;
using System.Threading;

namespace ManualResetEventDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 通过构造函数初始化
            // false表示“初始状态为关门”,设置为true则表示初始状态为开门
            ManualResetEvent mre = new ManualResetEvent(false);

            // 创建一个线程
            Thread t1 = new Thread(() =>
            {
                // 因为初始化的状态为关门
                Console.WriteLine("开始等着开门");
                // 调用等着开门的方法,只有开门以后才会执行下面的代码
                mre.WaitOne();
                // 开门之后才会执行这句代码
                Console.WriteLine("终于开门了");
            });

            // 启动线程
            t1.Start();

            Console.WriteLine("按任意键开门");
            Console.ReadLine();
            // 调用开门的方法
            mre.Set();
            // 休眠3秒
            Thread.Sleep(3000);
            // 关门
            mre.Reset();
            Console.WriteLine("关门了");

            Console.ReadKey();

        }
    }
}

程序输出结果:

WaitOne()方法还可以设置等待超时时间,超过了等待时间就不会再等了。如果不设置等待超时时间,那么就会一直等下去。看下面代码:

using System;
using System.Threading;

namespace ManualResetEventDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 通过构造函数初始化
            // false表示“初始状态为关门”,设置为true则表示初始状态为开门
            ManualResetEvent mre = new ManualResetEvent(false);

            //// 创建一个线程
            //Thread t1 = new Thread(() =>
            //{
            //    // 因为初始化的状态为关门
            //    Console.WriteLine("开始等着开门");
            //    // 调用等着开门的方法,只有开门以后才会执行下面的代码
            //    mre.WaitOne();
            //    // 开门之后才会执行这句代码
            //    Console.WriteLine("终于开门了");
            //});

            // 创建一个线程
            Thread t1 = new Thread(() =>
            {
                // 因为初始化的状态为关门
                Console.WriteLine("开始等着开门");
                // 设置超时等待时间为5秒钟
                if(mre.WaitOne(5000))
                {
                    // 开门之后才会执行这句代码
                    Console.WriteLine("终于开门了");
                }
                else
                {
                    // 等待超时
                    Console.WriteLine("过了5秒钟了还没开门");
                }

            });

            // 启动线程
            t1.Start();

            Console.WriteLine("按任意键开门");
            Console.ReadLine();
            // 调用开门的方法
            mre.Set();
            // 休眠3秒
            Thread.Sleep(3000);
            // 关门
            mre.Reset();
            Console.WriteLine("关门了");

            Console.ReadKey();

        }
    }
}

运行程序,什么也不输入,看运行结果:

WaitHandle.WaitAll(WaitHandle[] waitHandles)用来等待所有信号都变为“开门状态”,WaitHandle.WaitAny(WaitHandle[] waitHandles) 用来等待任意一个信号都变为“开门状态”。

ManualResetEvent是一旦设定Set()后就一直开门,除非调用Reset()方法关门。

举一个形象点的例子,ManualResetEvent就相当于是学校的大门,只要开门以后大家都可以进入,除非主动关门(等于执行了Reset()方法)。

原文地址:https://www.cnblogs.com/dotnet261010/p/12339649.html

时间: 2024-08-04 03:22:41

多线程:线程同步之WaitHandle的相关文章

多线程&线程同步

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

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

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

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

线程同步: 为什么需要同步 ①   线程同步是为了防止多个线程访问一个数据对象时,对数据造成破坏. ②   线程的同步是保证多线程安全访问竞争资源的一种手段. 同步和锁 ①   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,于是它也执行售出操作,这样就会产生负数.所以在编写多线程程序时,应该考虑到线程安全问题

【多线程-线程同步】

线程同步:协调多个线程间的并发操作,以获得符合预期的,确定的执行结果,消除多线程应用程序的不确定性. 使用线程的同步:可以保护资源同时只能由一个线程访问,一般采取的措施是获取锁,释放锁.即锁机制:可以协调线程的访问顺序,即某一资源只能先由线程A访问,再由线程B进行访问. class Program { private static Thread subthread ; private static int i; static void Main(string[] args) { subthrea