简体   繁体   English

让许多工作线程等待主线程的最佳方法,反之亦然

[英]Best way to let many worker-threads wait for mainthread and vice versa

I'm looking for a fast way to let many worker threads wait for an event to continue and block the main thread until all worker threads are finished. 我正在寻找一种快速方法让许多工作线程等待事件继续并阻塞主线程直到所有工作线程都完成。 I first used TPL or AutoResetEvent but since my calculation isn't that expensive the overhead was way too much. 我第一次使用TPL或AutoResetEvent,但由于我的计算并不昂贵,所以开销太大了。

I found a pretty interesting article concerning this problem and got great results (using only one worker thread) with the last synchronization solution (Interlocked.CompareExchange). 我发现了一篇关于这个问题的非常有趣的文章 ,并且使用最后一个同步解决方案(Interlocked.CompareExchange)获得了很好的结果(仅使用一个工作线程)。 But I don't know how to utilize it for a scenario where many threads wait for one main tread repeatedly. 但我不知道如何利用它来解决许多线程反复等待一个主要步骤的情况。

Here is an example using single thread, CompareExchange, and Barrier: 以下是使用单线程,CompareExchange和Barrier的示例:

static void Main(string[] args)
{
    int cnt = 1000000;

    var stopwatch = new Stopwatch();

    stopwatch.Start();
    for (int i = 0; i < cnt; i++) { }
    Console.WriteLine($"Single thread: {stopwatch.Elapsed.TotalSeconds}s");

    var run = true;
    Task task;

    stopwatch.Restart();
    int interlock = 0;
    task = Task.Run(() =>
    {
        while (run)
        {
            while (Interlocked.CompareExchange(ref interlock, 0, 1) != 1) { Thread.Sleep(0); }
            interlock = 2;
        }
        Console.WriteLine($"CompareExchange synced: {stopwatch.Elapsed.TotalSeconds}s");
    });

    for (int i = 0; i < cnt; i++)
    {
        interlock = 1;
        while (Interlocked.CompareExchange(ref interlock, 0, 2) != 2) { Thread.Sleep(0); }
    }
    run = false;
    interlock = 1;
    task.Wait();

    run = true;
    var barrier = new Barrier(2);
    stopwatch.Restart();
    task = Task.Run(() =>
      {
          while (run) { barrier.SignalAndWait(); }
          Console.WriteLine($"Barrier synced: {stopwatch.Elapsed.TotalSeconds}s");
      });

    for (int i = 0; i < cnt; i++) { barrier.SignalAndWait(); }
    Thread.Sleep(0);
    run = false;
    if (barrier.ParticipantsRemaining == 1) { barrier.SignalAndWait(); }
    task.Wait();

    Console.ReadKey();
}

Average results (in seconds) are: 平均结果(以秒为单位)是:

Single thread: 0,002 CompareExchange: 0,4 Barrier: 1,7 单线程:0,002比较交换:0,4障碍:1,7

As you can see Barriers' overhead seems to be arround 4 times higher! 正如你所看到的,障碍的开销似乎高出4倍! If someone can rebuild me the CompareExchange-scenario to work with multiple worker threads this would surely help, too! 如果有人可以重建我的CompareExchange方案来处理多个工作线程,这肯定也会有所帮助!

Sure, 1 second overhead for a million calculations is pretty less! 当然,一百万次计算的1秒开销相当少! Actually it just interests me. 其实它只是让我感兴趣。

Edit: 编辑:

System.Threading.Barrier seems to be the fastest solution for this scenario. System.Threading.Barrier似乎是这种情况下最快的解决方案。 For saving a double blocking (all workers ready for work, all workes finished) I used the following code for the best results: 为了保存双重阻止(所有工作人员都准备好工作,所有工作都已完成),我使用以下代码获得最佳结果:

while(work)
{
    while (barrier.ParticipantsRemaining > 1) { Thread.Sleep(0); }
    //Set work package
    barrier.SignalAndWait()
}

It seems like you might want to use a Barrier to synchronise a number of workers with a main thread. 您似乎可能希望使用Barrier来将多个工作程序与主线程同步。

Here's a compilable example. 这是一个可编辑的例子。 Have a play with it, paying attention to when the output tells you that you can "Press <Return> to signal the workers to start". 玩弄它,注意当输出告诉你你可以“按<Return>指示工人开始”。

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            print("Main thread is starting the workers.");
            int numWorkers = 10;

            var barrier = new Barrier(numWorkers + 1); // Workers + main (controlling) thread.

            for (int i = 0; i < numWorkers; ++i)
            {
                int n = i; // Prevent modified closure.
                Task.Run(() => worker(barrier, n));
            }

            while (true)
            {
                print("***************** Press <RETURN> to signal the workers to start");
                Console.ReadLine();

                print("Main thread is signalling all the workers to start.");

                // This will wait for all the workers to issue their call to
                // barrier.SignalAndWait() before it returns:

                barrier.SignalAndWait(); 

                // At this point, all workers AND the main thread are at the same point.
            }
        }

        static void worker(Barrier barrier, int workerNumber)
        {
            int iter = 0;

            while (true)
            {
                print($"Worker {workerNumber} on iteration {iter} is waiting for barrier.");

                // This will wait for all the other workers AND the main thread 
                // to issue their call to barrier.SignalAndWait() before it returns:

                barrier.SignalAndWait();

                // At this point, all workers AND the main thread are at the same point.

                int delay = randomDelayMilliseconds();
                print($"Worker {workerNumber} got barrier, now sleeping for {delay}");
                Thread.Sleep(delay);

                print($"Worker {workerNumber} finished work for iteration {iter}.");
            }
        }

        static void print(string message)
        {
            Console.WriteLine($"[{sw.ElapsedMilliseconds:00000}] {message}");
        }

        static int randomDelayMilliseconds()
        {
            lock (rng)
            {
                return rng.Next(10000) + 5000;
            }
        }

        static Random    rng = new Random();
        static Stopwatch sw  = Stopwatch.StartNew();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 将类转换为xml的最佳方法是什么,反之亦然 - what is the best way to convert a class to xml and vice versa 有什么方法可以通过 http 请求在 API 和 Worker 服务之间进行通信(反之亦然)? - Is there any way to communicate between API and Worker service via http request (and vice versa)? 有多少管理到本地(反之亦然)的转换? - How many managed to native (and vice versa) transitions? C#有符号和无符号整数到Big Endian字节数组,反之亦然,使用具有“最佳”性能的Bitwise方式 - C# Signed & Unsigned Integral to Big Endian Byte Array, and vice versa using Bitwise way with “best” performance 将Posix线程转换为C#线程,反之亦然 - Converting Posix Threads to C# Threads and vice versa 创建多个后台工作线程的最佳方法是什么? - What is the best way to create several background worker threads? 将小价值映射到大价值,反之亦然 - Way to Map Small Values to Large and Vice Versa 在.Net / C#中,当我所有的工作线程都已完成处理时,有没有办法等待或得到通知? - In .Net/C# is there a way to either wait for or get notified when all my worker threads have completed processing? Java 或 C# 十六进制/十进制转换,反之亦然,如何最好? - Java or C# sexagesimal/decimal conversion and vice versa, How is best? NHibernate:将默认值视为null的简单方法(反之亦然) - NHibernate: Simple way to treat default values as null (or vice versa)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM