深入多线程之:解析线程的交会(Thread Rendezvous)详解

在上篇文章中我们使用了Wait和Pulse 实现了Countdown

接下来我们可以使用刚刚写的Countdown 类来实现两个线程的交会。


代码如下:

class Rendezvous
    {
        static object _locker = new object();
        static Countdown _countdown = new Countdown(2);

public static void MainThread()
        {
            Random r = new Random();
            new Thread(Mate).Start(r.Next(1000));
            Thread.Sleep(r.Next(10000)); //主线程睡眠一段时间

_countdown.Singnal(); //向_countdown注册信号,告知主线程已经来了。
            _countdown.Wait();    //等待其他线程

Console.WriteLine("Mate!");
        }

static void Mate(object delay)
        {
            Thread.Sleep((int)delay);//线程睡眠。

_countdown.Singnal(); //向_countdown注册信号,告知线程已经来了。
            _countdown.Wait();   //等待其他线程。

Console.WriteLine("Mate!");

}
}

就像小时候去春游一样,这里的_countdown就是老师,线程就是学生。

学生A早上睡觉,然后起床来到交会点,然后告诉老师,我来了,接着等待老师的出发命令,因为老师知道有两个学生要去春游,所以现在只来了一个,还有一个没有来,所以老师会让学生A等待,阻塞。

学生B也是睡觉,接着也来到交会点,告诉老师,我也来了,然后等待老师的出发命令。

当学生B告诉老师我来了的时候,此时老师的剩余等待学生计数为0,所以老师告诉这两个学生,你们可以出发了。

.net framework 4.0 提供了Barrier 的构造来实现线程交会的功能。如图所示:

Thread1 调用SignalAndWait告知Barrier,我已经来了,然后阻塞。

Thread3调用SignalAndWait告知Barrier,我已经来了,然后阻塞。

Thread2 调用SignalAndWait告知Barrier,我已经来了,Barrier知道现在三个线程都来了,所以让他们继续并发执行。

Barrier的方法简介:

AddParticipants:增加参与者,也就是增加春游的人数。
    RemoveParticipant:减少参与者,可能某人肚子痛,不能参加春游了。
    SignalAndWait :参与者已经来了,并等待其他参与者的到来。

下面是使用Barrier的示例:


代码如下:

static Barrier _barrier = new Barrier(3);//说明有三个参与者

public static void Main()
        {
            new Thread(Speak).Start();
            new Thread(Speak).Start();
            new Thread(Speak).Start();
        }

static void Speak()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.Write(i + " ");
                _barrier.SignalAndWait(); //告知参与者已经来了,等待其他参与者
            }
        }

输出如下:

0 0 0 1 1 1 2 2 2 3 3 3 4 4 4

Barrier 的另一个非常有用的特性是在每一个阶段完成的时候你都可以执行一个post-phase 的action委托。

什么是阶段呢??,阶段就是参与者全部都到了的时候。

如果我们修改Barrier的构造函数如下:

static Barrier _barrier = new Barrier(3, (barrier) => Console.WriteLine());

//说明有三个参与者,并且每次三个参与者完成任务的时候执行Console.WriteLine方法.

那么我们的输出如下所示:

0 0 0

1 1 1

2 2 2

3 3 3

4 4 4

(0)

相关推荐

  • .NET中STAThread的使用详解

    在WindowForm应用程序中主要的线程,是采用一种称为「Single-Threaded Apartment(STA)」的线程模型.这个STA线程模型,在线程内加入了讯息帮浦等等机制,减少开发人员撰写窗口程序的工作量. 而在开发类别库的时候,如果要使用类似的STA线程模型,可以使用下列的程序代码提供的类别来完成. 复制代码 代码如下: namespace CLK.Threading{    public class STAThread    {        // Enum        pr

  • C++线程优先级SetThreadPriority的使用实例

    本文实例讲述了C++线程优先级SetThreadPriority的使用方法,分享给大家供大家参考.具体方法如下: 复制代码 代码如下: // ThreadPriority.cpp : 定义控制台应用程序的入口点.  //    #include "stdafx.h"  #include <Windows.h>    DWORD WINAPI ThreadProcIdle(LPVOID lpParameter)  {      for (int i=0;i<20;i++

  • C++ AfxBeginThread的介绍/基本用法

    AfxBeginThread  用户界面线程和工作者线程都是由AfxBeginThread创建的.现在,考察该函数:MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,分别有如下的原型和过程: 用户界面线程的AfxBeginThread 用户界面线程的AfxBeginThread的原型如下: CWinThread* AFXAPI AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority, U

  • C++封装远程注入类CreateRemoteThreadEx实例

    本文实例讲述了C++封装远程注入类CreateRemoteThreadEx的方法,分享给大家供大家参考.具体方法如下: 首先,类初始化时传入要注入的DLL文件名 只使用两个函数 复制代码 代码如下: // 注入DLL到指定的地址空间 BOOL InjectModuleInto(DWORD dwProcessId); // 从指定的地址空间卸载DLL BOOL EjectModuleFrom(DWORD dwProcessId); .h头文件如下: 复制代码 代码如下: #pragma once 

  • .NET Windows 多线程thread编程

    进程:工厂搬砖头,10个工人搬完1000个砖头 线程:每个工人,搬完100个砖头,就是一个任务 采用线程,异步搬: 手工去搬,10个工人同时搬,效率高,异步执行,如果不采用线程,等第一个人搬完后第二个人才搬,那就慢了 什么时候不能用线程: 如果要用小车来搬,只有一个小车,那么,在这样的情况下,就不应该用线程,因为只有一个小车,各个线程都在争夺和等待这个小车,所以不能用线程.如果这个时候还要用线程,则导致资源浪费, 因为每个线程都有自己的资源,如包工头(CPU)同一时间要管理这10个工人,而只有一

  • C++实现CreatThread函数主线程与工作线程交互的方法

    本文实例讲述了C++开启线程CreatThread函数的使用,实现主线程与工作线程交互的功能.分享给大家供大家参考. 具体实现代码如下: 复制代码 代码如下: //线程函数  DWORD WINAPI ThreadProc(LPVOID lpParameter)  {      for (int i=0;i<20;i++)      {          printf("I'm in thread,count=%d\n",i);      }      return 0;  } 

  • 深入多线程之:解析线程的交会(Thread Rendezvous)详解

    在上篇文章中我们使用了Wait和Pulse 实现了Countdown 接下来我们可以使用刚刚写的Countdown 类来实现两个线程的交会. 复制代码 代码如下: class Rendezvous    {        static object _locker = new object();        static Countdown _countdown = new Countdown(2); public static void MainThread()        {      

  • Java 解析线程的几种状态详解

    目录 1.线程的5种状态 2.Java线程的6种状态 3.Java线程状态的转换 总结 1. 线程的5种状态 从操作系统层面上,任何线程一般都具有五种状态,即创建.就绪.运行.阻塞.终止. (1) 新建状态(NEW) 在程序中用构造方法创建一个新线程时,如new Thread(),该线程就是创建状态,此时它已经有了相应的内存空间和其它资源,但是还没有开始执行. (2) 就绪状态(READ) 新建线程对象后,调用该线程的start()方法就可以启动线程.当线程启动时,线程就进入就绪状态(runna

  • Java线程池FutureTask实现原理详解

    前言 线程池可以并发执行多个任务,有些时候,我们可能想要跟踪任务的执行结果,甚至在一定时间内,如果任务没有执行完成,我们可能还想要取消任务的执行,为了支持这一特性,ThreadPoolExecutor提供了 FutureTask 用于追踪任务的执行和取消.本篇介绍FutureTask的实现原理. 类视图 为了更好的理解FutureTask的实现原理,这里先提供几个重要接口和类的结构,如下图所示: RunnableAdapter ThreadPoolExecutor提供了submit接口用于提交任

  • Java ThreadLocal原理解析以及应用场景分析案例详解

    目录 ThreadLocal的定义 ThreadLocal的应用场景 ThreadLocal的demo TheadLocal的源码解析 ThreadLocal的set方法 ThreadLocal的get方法 ThreadLocalMap的结构 ThreadLocalMap的set方法 ThreadLocalMap的getEntry方法 ThreadLocal的内存泄露 如何避免内存泄露呢 应用实例 实际应用二 总结 ThreadLocal的定义 JDK对ThreadLocal的定义如下: The

  • 线上Spring CPU 高负载解决思路详解

    目录 引言 定位问题 日志搜索 监控看板 ThreadDump 优化 事后反思 引言 背景: 在某一天,运营同事突然发现运营看板好几天没有更新数据了, 然后找了过来?! 这里看似抛出了一个问题 ? 但细想一下, 同时暴露了我们对于线上服务的监控未完全覆盖到!!! 这是致命的!!! 当然, 这篇文章先不讨论监控的问题, 后面会推出完善的监控方案 定位问题 问题抛过来了, 那么我们第一步要怎样做呢? 拿到问题的第一步, 先理解题意, 这里有几个关键的信息点 第一 : 好几天, 具体哪一天, 这个后面

  • Golang并发编程之main goroutine的创建与调度详解

    目录 0. 简介 1. 创建main goroutine 2. 调度main goroutine 0. 简介 上一篇博客我们分析了调度器的初始化,这篇博客我们正式进入main函数及为其创建的goroutine的过程分析. 1. 创建main goroutine 接上文,在runtime/asm_amd64.s文件的runtime·rt0_go中,在执行完runtime.schedinit函数进行调度器的初始化后,就开始创建main goroutine了. // create a new goro

  • java并发编程_线程池的使用方法(详解)

    一.任务和执行策略之间的隐性耦合 Executor可以将任务的提交和任务的执行策略解耦 只有任务是同类型的且执行时间差别不大,才能发挥最大性能,否则,如将一些耗时长的任务和耗时短的任务放在一个线程池,除非线程池很大,否则会造成死锁等问题 1.线程饥饿死锁 类似于:将两个任务提交给一个单线程池,且两个任务之间相互依赖,一个任务等待另一个任务,则会发生死锁:表现为池不够 定义:某个任务必须等待池中其他任务的运行结果,有可能发生饥饿死锁 2.线程池大小 注意:线程池的大小还受其他的限制,如其他资源池:

  • java 可重启线程及线程池类的设计(详解)

    了解JAVA多线程编程的人都知道,要产生一个线程有两种方法,一是类直接继承Thread类并实现其run()方法:二是类实现Runnable接口并实现其run()方法,然后新建一个以该类为构造方法参数的Thread,类似于如下形式: Thread t=new Thread(myRunnable).而最终使线程启动都是执行Thread类的start()方法. 在JAVA中,一个线程一旦运行完毕,即执行完其run()方法,就不可以重新启动了.此时这个线程对象也便成了无用对象,等待垃圾回收器的回收.下次

  • Java线程的生命周期的详解

    Java线程的生命周期的详解 对于多线程编程而言,理解线程的生命周期非常重要,本文就针对这一点进行讲解. 一.线程的状态 线程的存在有几种不同的状态,如下: New状态 Ready状态 Running状态 Dead状态 Non Runnable状态 1.New状态 New状态是线程已经被创建,但是还未开始运行的状态.此状态通过调用线程的start()方法可让线程运行. 2.Runnable状态 Runnable状态可称为准备运行状态,也可称为队列,此状态通过调用线程的start()方法可让线程运

  • 基于java 线程的几种状态(详解)

    线程可以有六种状态: 1.New(新创建) 2.Runnable(可运行)(运行) 3.Blocked(被阻塞) 4.Waiting(等待) 5.Timed waiting(计时等待) 6.Terminated(被终止) 新创建线程: 当用new操作符创建一个新线程时,如new Thread(r),该线程还没有开始运行,它的当前状态为new,在线程运行之前还有一些基础工作要做. 可运行线程: 一旦线程调用start方法,线程处于runnable状态.在这个状态下的线程可能正在运行也可能没有运行(

随机推荐