• C# 多线程通信详解


    一、WaitHandler的类层次

    image

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

    image

    image

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

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

    image

    image

    image

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

    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("所有任务都已经完成了,我不用再等待了。");
    });
    运行结果如下:

    image

    二、EventWaitHandle

    image

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

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

    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();
    });
    运行结果如下:

    image

    那么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();
    }

    运行结果

    image

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

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

    image

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

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

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

    Semaphore sempore = new Semaphore(0, 3);
    for (int i = 0; i < 8; i++)
    {
        ThreadPool.QueueUserWorkItem(ar =>
        {
            sempore.WaitOne();
            AppCenter.AppendLog("	第:" +((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);
        }
    });

    运行结果:

    image

  • 相关阅读:
    [转] 接触C# 反射 2
    [转] C#操作Excel文件
    【Leetcode】Path Sum II
    java通用抹去魔,在边界行动,擦除补偿
    python抓取网络内容
    一个合格的程序猿编程
    Android的相关的源代码的方法
    随笔
    使用方便git命令检查记录的版本号
    opengl 扳回一球
  • 原文地址:https://www.cnblogs.com/binfire/p/5045032.html
Copyright © 2020-2023  润新知