由于某些原因,我必须坚持使用.NET 3.5,我需要一个来自.NET 4的Barrier类功能。我有一堆线程可以完成一些工作,我希望它们相互等待,直到完成所有工作。当一切都完成后,我希望他们以类似的方式一次又一次地完成工作。 受到线程Difference between Barrier in C# 4.0 and WaitHandle in C# 3.0?的鼓励,我决定使用AutoResetEvent和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);
}
}
}
我收到的输出如下:
'屏障'处的线程0将睡眠7564 '屏障'处的线程1将睡眠5123 '屏障'处的线程2将睡眠4237 线圈2在'障碍'处有时间4237 在'障碍'处的线程1具有时间5123 在'障碍'处的线程0与时间7564 '屏障'处的线程0将睡眠8641 '障碍'处的线程0,时间为8641
就是这样。在最后一行之后没有更多输出,应用程序不会终止。看起来有某种僵局。但是找不到问题。欢迎任何帮助。
谢谢!
答案 0 :(得分:5)
那是因为您使用AutoResetEvent。其中一个线程的WaitAll()调用将首先完成。这会自动导致所有ARE上的Reset()。这可以防止其他线程完成WaitAll()调用。
这里需要一个ManualResetEvent。
答案 1 :(得分:2)
下载适用于.NET 3.5的Reactive Extensions backport。您将找到Barrier
类以及.NET 4.0中发布的其他有用的并发数据结构和同步机制。
答案 2 :(得分:2)
以下是我用于XNA game的实现。当我写这篇文章时,屏障无法使用,而我仍然坚持使用.Net 3.5。它需要三组ManualResetEvents和一个计数器数组来保持相位。
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 SetupThreads()
{
// Make an NThreadGate for N threads.
this.mMyThreadGate = new NThreadGate(Environment.ProcessorCount);
// Make some threads...
// e.g. new Thread(new ThreadStart(this.DoWork);
}
线程工作者方法:
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);
}
}