C# 如何在.NET3.5中实现.NET4功能中的Barrier类

C# 如何在.NET3.5中实现.NET4功能中的Barrier类,c#,multithreading,barrier,cyclicbarrier,C#,Multithreading,Barrier,Cyclicbarrier,出于某些原因,我必须坚持使用.NET3.5,并且我需要.NET4中的Barrier类功能。我有一堆线程做一些工作,我希望它们互相等待,直到所有的工作都完成。当所有的工作都完成后,我希望他们以同样的方式一次又一次地做这项工作。 在线程的鼓励下,我决定用AutoResteEvent和WaitHandle类实现Barrier功能。 尽管我的代码遇到了问题: class Program { const int numOfThreads = 3; static AutoResetEven

出于某些原因,我必须坚持使用.NET3.5,并且我需要.NET4中的Barrier类功能。我有一堆线程做一些工作,我希望它们互相等待,直到所有的工作都完成。当所有的工作都完成后,我希望他们以同样的方式一次又一次地做这项工作。 在线程的鼓励下,我决定用AutoResteEvent和WaitHandle类实现Barrier功能。 尽管我的代码遇到了问题:

class Program
{
    const int numOfThreads = 3;

    static AutoResetEvent[] barrier = new AutoResetEvent[numOfThreads];
    static Random random = new Random(System.DateTime.Now.Millisecond);

    static void barriers2(object barrierObj)
    {
        AutoResetEvent[] barrierLocal = (AutoResetEvent[])barrierObj;
        string name = Thread.CurrentThread.Name;
        for (int i = 0; i < 10; i++)
        {
            int sleepTime = random.Next(2000, 10000);
            System.Console.Out.WriteLine("Thread {0} at the 'barrier' will sleep for {1}.", name, sleepTime);
            Thread.Sleep(sleepTime);
            System.Console.Out.WriteLine("Thread {0} at the 'barrier' with time {1}.", name, sleepTime);
            int currentId = Convert.ToInt32(name);
            //for(int z = 0; z < numOfThreads; z++)
                barrierLocal[currentId].Set();
            WaitHandle.WaitAll(barrier);
            /*
            for (int k = 0; k < numOfThreads; k++)
            {
                if (k == currentId)
                {
                    continue;
                }
                System.Console.Out.WriteLine("Thread {0} is about to wait for the singla from thread: {1}", name, k);
                barrierLocal[k].WaitOne();
                System.Console.Out.WriteLine("Thread {0} is about to wait for the singla from thread: {1}. done", name, k);
            }
            */
        }
    }

    static void Main(string[] args)
    {
        for (int i = 0; i < numOfThreads; i++)
        {
            barrier[i] = new AutoResetEvent(false);
        }
        for (int i = 0; i < numOfThreads; i++)
        {
            Thread t = new Thread(Program.barriers2);
            t.Name = Convert.ToString(i);
            t.Start(barrier);
        }
    }
}
类程序
{
常量int numOfThreads=3;
static AutoResetEvent[]barrier=新的AutoResetEvent[numOfThreads];
静态随机=新随机(System.DateTime.Now.毫秒);
静态空隙屏障2(对象屏障BJ)
{
自动还原事件[]barrierLocal=(自动还原事件[])barrierObj;
字符串名称=Thread.CurrentThread.name;
对于(int i=0;i<10;i++)
{
int sleepTime=random.Next(200010000);
System.Console.Out.WriteLine(“barrier”处的线程{0}将睡眠{1}。”,名称,睡眠时间);
睡眠(睡眠时间);
System.Console.Out.WriteLine(“线程{0}位于时间为{1}的“屏障”处”,名称,休眠时间);
int currentId=Convert.ToInt32(名称);
//对于(intz=0;z
我收到的输出如下:

“屏障”处的线程0将休眠7564秒 “屏障”处的线程1将休眠5123 “屏障”处的线程2将休眠4237秒 时间为4237的“屏障”处的螺纹2 时间为5123的“屏障”处的螺纹1 线程0位于“屏障”处,时间为7564 “屏障”处的线程0将休眠8641 时间为8641的“屏障”处的线程0

就这样。在最后一行之后,没有更多的输出,应用程序也不会终止。看起来好像出现了某种僵局。但是找不到问题。欢迎任何帮助


谢谢

那是因为您使用AutoResteEvent。线程的一个WaitAll()调用将首先完成。这会自动在所有战神上重置()。这会阻止其他线程完成其WaitAll()调用


此处需要手动重置事件。

下载.NET 3.5的后端口。您将发现
Barrier
类以及.NET 4.0中发布的其他有用的并发数据结构和同步机制。

以下是我的应用程序实现。当我写这篇文章时,屏障是不可用的,我仍然坚持使用.NET3.5。它需要三组手动复位事件和一个计数器阵列来保持相位

using System;
using System.Threading;

namespace Colin.Threading
{
    /// <summary>
    /// Threading primitive for "barrier" sync, where N threads must stop at certain points 
    /// and wait for all their bretheren before continuing.
    /// </summary>
    public sealed class NThreadGate
    {
        public int mNumThreads;
        private ManualResetEvent[] mEventsA;
        private ManualResetEvent[] mEventsB;
        private ManualResetEvent[] mEventsC;
        private ManualResetEvent[] mEventsBootStrap;
        private Object mLockObject;
        private int[] mCounter;
        private int mCurrentThreadIndex = 0;

        public NThreadGate(int numThreads)
        {
            this.mNumThreads = numThreads;

            this.mEventsA = new ManualResetEvent[this.mNumThreads];
            this.mEventsB = new ManualResetEvent[this.mNumThreads];
            this.mEventsC = new ManualResetEvent[this.mNumThreads];
            this.mEventsBootStrap = new ManualResetEvent[this.mNumThreads];
            this.mCounter = new int[this.mNumThreads];
            this.mLockObject = new Object();

            for (int i = 0; i < this.mNumThreads; i++)
            {
                this.mEventsA[i] = new ManualResetEvent(false);
                this.mEventsB[i] = new ManualResetEvent(false);
                this.mEventsC[i] = new ManualResetEvent(false);
                this.mEventsBootStrap[i] = new ManualResetEvent(false);
                this.mCounter[i] = 0;
            }
        }

        /// <summary>
        /// Adds a new thread to the gate system.
        /// </summary>
        /// <returns>Returns a thread ID for this thread, to be used later when waiting.</returns>
        public int AddThread()
        {
            lock (this.mLockObject)
            {
                this.mEventsBootStrap[this.mCurrentThreadIndex].Set();
                this.mCurrentThreadIndex++;
                return this.mCurrentThreadIndex - 1;
            }
        }

        /// <summary>
        /// Stop here and wait for all the other threads in the NThreadGate. When all the threads have arrived at this call, they
        /// will unblock and continue.
        /// </summary>
        /// <param name="myThreadID">The thread ID of the caller</param>
        public void WaitForOtherThreads(int myThreadID)
        {
            // Make sure all the threads are ready.
            WaitHandle.WaitAll(this.mEventsBootStrap);

            // Rotate between three phases.
            int phase = this.mCounter[myThreadID];
            if (phase == 0)        // Flip
            {
                this.mEventsA[myThreadID].Set();
                WaitHandle.WaitAll(this.mEventsA);
                this.mEventsC[myThreadID].Reset();
            }
            else if (phase == 1)    // Flop
            {
                this.mEventsB[myThreadID].Set();
                WaitHandle.WaitAll(this.mEventsB);
                this.mEventsA[myThreadID].Reset();
            }
            else    // Floop
            {
                this.mEventsC[myThreadID].Set();
                WaitHandle.WaitAll(this.mEventsC);
                this.mEventsB[myThreadID].Reset();
                this.mCounter[myThreadID] = 0;
                return;
            }

            this.mCounter[myThreadID]++;
        }
    }
}
线程工作者方法:

private void DoWork()
{
    int localThreadID = this.mMyThreadGate.AddThread();

    while (this.WeAreStillRunning)
    {
        // Signal this thread as waiting at the barrier
        this.mMyThreadGate.WaitForOtherThreads(localThreadID);

        // Synchronized work here...

        // Signal this thread as waiting at the barrier
        this.mMyThreadGate.WaitForOtherThreads(localThreadID);

        // Synchronized work here...

        // Signal this thread as waiting at the barrier
        this.mMyThreadGate.WaitForOtherThreads(localThreadID);
    }
}

汉斯,谢谢你的回答。这可以解释问题,但在ManualResetEvent类中,必须有一些线程在所有线程通过“屏障”后重置MRE。你知道在我的场景中该怎么做吗?从阅读Barrier源代码中获得一些灵感,比如说,Reflector。我认为它使用两组MRE(命名为奇数和偶数)并在它们之间交替。您认为这样一个简单的解决方案怎么样:
lock(obj){threadscont++;if(threadscont==numOfThreads){System.Console.WriteLine(“所有线程都已完成。”);threadScont=0;Monitor.pulsell(obj);}else{Monitor.Wait(obj);}您好,谢谢您的回答。是否可以从这个后端口获取Barrier类,或者我必须始终引用整个包?谢谢。问题是,它是为了一个大型项目的目的,不能强迫其他很多人在生产环境中安装extenstion+。您不必安装RX framework on目标计算机。事实上,如果不想,您不必在开发计算机上安装它。只需获取它附带的System.Threading.dll库,并像其他任何第三方库一样引用它。我就是这么做的。
private void DoWork()
{
    int localThreadID = this.mMyThreadGate.AddThread();

    while (this.WeAreStillRunning)
    {
        // Signal this thread as waiting at the barrier
        this.mMyThreadGate.WaitForOtherThreads(localThreadID);

        // Synchronized work here...

        // Signal this thread as waiting at the barrier
        this.mMyThreadGate.WaitForOtherThreads(localThreadID);

        // Synchronized work here...

        // Signal this thread as waiting at the barrier
        this.mMyThreadGate.WaitForOtherThreads(localThreadID);
    }
}