我一直致力于使用HTTP与客户端进行通信的自定义多线程服务器。要创建新线程,我一直在使用Task.Factory.StartNew()方法。为了同步,我一直在使用ManualResetEvent对象。
这是代码的本质:
namespace ThreadTest {
class Program {
private readonly ManualResetEvent _event = new ManualResetEvent(false);
public void Start() {
for (int i = 0; i < 100; i++) {
int num = i;
Task.Factory.StartNew(() => {
Console.WriteLine("Task {0} Started", num);
_event.WaitOne();
});
}
}
static void Main(string[] args) {
var test = new Program();
test.Start();
Console.ReadLine();
}
}
}
在实际代码中,_event
对象最终会调用Set
来释放等待任务。
我遇到的问题是这个代码在我的开发机器上运行正常(它是双核,每个核心有2个线程并使用MS .NET 4运行时),但在我的服务器上不起作用(这是单核,每个核心有1个线程并使用Mono 2.8运行时)。我的开发机器上的输出是:
Task 0 Started
Task 1 Started
Task 3 Started
Task 2 Started
Task 4 Started
Task 5 Started
...
服务器输出
Task 53 Started
我的问题是:我对ManualResetEvent或Tasks有什么误解?为什么TaskFactory继续在双核而不是单核上创建任务?
更新
我刚刚在我的双核(每核心1个线程)mac笔记本电脑上尝试过,我得到了输出
Task 97 Started
Task 1 Started
然后什么都没有。我也在我的开发机器(4个线程)上尝试使用Mono并获得:
Task 99 Started
Task 2 Started
Task 98 Started
Task 0 Started
所以它看起来像是Mono的一个错误。
答案 0 :(得分:2)
原来这是Mono的一个错误。我filed a report现在已经修好了。
答案 1 :(得分:0)
您提供的代码永远不会完成任何任务,因为事件永远不会发出信号。最终它耗尽线程来分配并锁定。问题实际上是“当没有任何线程可以继续时,任务并行库在不同的CPU配置下启动了多少线程”,因为这是您的代码有效测量的。
任务并行库的默认调度程序基于ThreadPool
,它使用爬山算法调整线程数以最大化工作项完成率。该算法会考虑您拥有多少核心。
有更好的方法可以使用任务并行库来安排任务完成后要完成的工作,这些方法不会占用线程,例如ContinueWith
。你想用事件构造实现什么目标?