C# 多线程通信详解

一、WaitHandler的类层次

可以看到 WaitHandle是 事件(EventWaitHandle)、互斥体(Mutex)、信号量(Sempahore)的父类。

WaitHandle我们最经常使用的方法,并是使用它的静态方法WaitAll. 我们会发现在这个WaitHandle里面只有等待方法,也就是它会阻塞当前线程的执行。

那么如何要解除它对当前线程的阻塞呢,那么就需要依赖于各个子类的方法了。

例如现在有一个这样的场景,如何在一个方法中,等待所有的线程全部执行完,最后再统计得到的计算结果呢?

WaitHandle[] handlers = new WaitHandle[]{
  new AutoResetEvent(false),
  new AutoResetEvent(false),
  new AutoResetEvent(false),
  new AutoResetEvent(false),
  new AutoResetEvent(false),
  new AutoResetEvent(false),
  new AutoResetEvent(false),
  new AutoResetEvent(false)
};

for (var i = 0; i < handlers.Length; i++)
{
    ThreadPool.QueueUserWorkItem(ar =>
    {
        int index = (int)ar;
        Thread.Sleep(1000);
        AppCenter.AppendLog("任务:" + index + "开始执行!");
        (handlers[index] as AutoResetEvent).Set();
    }, i);
}

ThreadPool.QueueUserWorkItem(ar =>
{
    WaitHandle.WaitAll(handlers);
    AppCenter.AppendLog("所有任务都已经完成了,我不用再等待了。");
});

运行结果如下:

二、EventWaitHandle

这个方法,可以方便实现两个线程之间的相互通信。

如何实现两个线程的相互通信?

EventWaitHandle handleA = new AutoResetEvent(false);
EventWaitHandle handleB = new AutoResetEvent(false);

ThreadPool.QueueUserWorkItem(ar =>
{
    AppCenter.AppendLog("A:我是A,我已经开始运行了");
    Thread.Sleep(2000);
    AppCenter.AppendLog("A:我想睡觉了,B你先跑跑吧。");
    EventWaitHandle.SignalAndWait(handleB, handleA);
    AppCenter.AppendLog("A:开始工作ing");
    Thread.Sleep(3000);
    AppCenter.AppendLog("A:这个有点难,问下B");
    EventWaitHandle.SignalAndWait(handleB, handleA);
    AppCenter.AppendLog("A:不错,今天任务搞定,我也闪人了。");
});

ThreadPool.QueueUserWorkItem(ar =>
{
    handleB.WaitOne();
    AppCenter.AppendLog("B:我是B,我已经顶替A开始运行了。");
    Thread.Sleep(5000);
    AppCenter.AppendLog("B:我的事情已经做完了,该让A搞搞了,休息一会。");
    EventWaitHandle.SignalAndWait(handleA, handleB);
    AppCenter.AppendLog("B:hi,A我搞定了,下班了。");
    handleA.Set();
});

运行结果如下:

那么AutoResetEvent和ManualResetEvent有什么区别呢?我们先做个实验。

private EventWaitHandle manualEvent = new ManualResetEvent(false);

private void ManualResetEvent_Click(object sender, EventArgs e)
{   

    AppCenter.CleanLogs();

    ThreadPool.QueueUserWorkItem(ar =>
    {
        int i = 0;
        while (true)
        {
            manualEvent.WaitOne();  //ManualResetEvent的Set()方法,让事件的终止状态永远为true,让这里一直能执行。
            i++;                    //而AutoResetEvent的Set()方法,初始化让这里执行一次,然后再次执行时是非终止的。将阻塞原有线程的执行
            AppCenter.AppendLog("#" + i.ToString());
            Thread.Sleep(1000);
        }
    });
}

private void button2_Click(object sender, EventArgs e)
{
    manualEvent.Set();
}

private void button3_Click(object sender, EventArgs e)
{
    manualEvent.Reset();
}

运行结果

我们会发现ManualResetEvent在触发Set()方法会,解除了原有的线程的 WaitOne方法,会一直打印输出。

而当我们替换为AutoResetEvent方法时候。

此时每次只会打印一个输出。因为它将 事件的状态设置为终止后,又变为了false.

三、Semaphore 控制并行线程的执行

应用场景,如果有多个线程跑,我能否每次控制3个线程一起跑呢。

Semaphore sempore = new Semaphore(0, 3);
for (int i = 0; i < 8; i++)
{
    ThreadPool.QueueUserWorkItem(ar =>
    {
        sempore.WaitOne();
        AppCenter.AppendLog("\t第:" +((int)ar).ToString() + "个开始运行.");
    },i);
}
ThreadPool.QueueUserWorkItem(ar =>
{
    for (int i = 0; i < 3; i++)
    {
        AppCenter.AppendLog("第" + (i + 1).ToString() + "批开始执行.");
        sempore.Release(3);
        Thread.Sleep(5000);
    }
});

运行结果:

时间: 2024-08-06 20:05:44

C# 多线程通信详解的相关文章

Java多线程编程详解

线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问. 由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synch

Java串口通信详解(转)

Java串口通信详解(转) 作者:denimcc 日期:2007-05-11 序言    说到开源,恐怕很少有人不挑大指称赞.学生通过开源代码学到了知识,程序员通过开源类库获得了别人的成功经验及能够按时完成手头的工程,商家通过开源软件赚到了钱……,总之是皆大欢喜.然而开源软件或类库的首要缺点就是大多缺乏详细的说明文档和使用的例子,或者就是软件代码随便你用,就是文档,例子和后期服务收钱.                                                        

多线程分离-详解

多线程分离-详解 在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached). 一个可结合的线程能够被其他线程收回其资源和杀死:在被其他线程回收之前,它的存储器资源(如栈)是不释放的. 相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放. 线程的分离状态决定一个线程以什么样的方式来终止自己.在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束. 只有当pthread_join()函数返回时,创建的线程才算终止

Android中实现java与PHP服务器(基于新浪云免费云平台)http通信详解

Android中实现java与PHP服务器(基于新浪云免费云平台)http通信详解 (本文转自: http://blog.csdn.net/yinhaide/article/details/44756989) 前言:现在很多APP都需要云的功能,也就是通过网络与服务器交换数据.有的采用tcp/ip协议,但是你必须拥有一个固定ip的服务器,可以购买阿里云服务器之类的,就是贵了点.如果只是个人的小应用的的话可以采用新浪云平台这种免费的服务器,采用的协议是http协议,具体实现方式如下: 方式一.在线

java网络编程Socket通信详解

Java最初是作为网络编程语言出现的,其对网络提供了高度的支持,使得客户端和服务器的沟通变成了现实,而在网络编程中,使用最多的就是Socket.像大家熟悉的QQ.MSN都使用了Socket相关的技术.下面就让我们一起揭开Socket的神秘面纱. Socket编程 一.网络基础知识(参考计算机网络)            关于计算机网络部分可以参考相关博客:           <TCP/IP协议栈及OSI参考模型详解> http://wangdy.blog.51cto.com/3845563/

Swift - 多线程GCD详解

//  GCD详解 //  目录: //  1. 创建GCD队列(最常用) //  2. 自定义创建队列 //  3. 使用多线程实现延迟加载 //  4. 使用多线程实现重复(循环) //  5. 使用多线程来控制 多任务数据加载 和 UI 的显示的先后顺序 //  6. 暂停队列 //  7. 恢复队列 // import UIKit class ViewController: UIViewController { @IBOutlet var myLable:UILabel? @IBActi

mysql5.6 基于GTID及多线程复制详解

一 GTID 详解 官方文档:http://dev.mysql.com/doc/refman/5.6/en/replication-gtids.html在这篇文档里,我们可以知道全局事务 ID 的官方定义是:GTID = source_id:transaction_id MySQL 5.6 中,每一个 GTID 代表一个数据库事务.在上面的定义中,source_id 表示执行事务的主库 uuid(server_uuid),transaction_id 是一个从 1 开始的自增计数,表示在这个主库

C++11多线程——lock详解

C++11提供了两种管理锁的类 std::lock_guard:与mutex RAII相关,方便线程对互斥量上锁 std::unique_lock:   与mutex RAII相关,方便线程对互斥量上锁,相比std::lock_guard提供了更好的上锁和解锁控制 一 lock_guard详解 lock_guard是一个模板类:template<classMutex>class lock_guard; lock_guard对象通常用来管理某个锁(Lock)对象,与Mutex RALL相关,方便

java多线程同步以及线程间通信详解&amp;消费者生产者模式&amp;死锁&amp;Thread.join()(多线程编程之二)

本篇我们将讨论以下知识点: 1.线程同步问题的产生 什么是线程同步问题,我们先来看一段卖票系统的代码,然后再分析这个问题: [java] view plain copy print? package com.zejian.test; /** * @author zejian * @time 2016年3月12日 下午2:55:42 * @decrition 模拟卖票线程 */ public class Ticket implements Runnable { //当前拥有的票数 private