我有一个线程,它创建可变数量的工作线程并在它们之间分配任务 . 这是通过向线程传递一个TaskQueue对象来解决的,您将在下面看到它的实现 .
这些工作线程只是迭代它们给出的TaskQueue对象,执行每个任务 .
private class TaskQueue : IEnumerable
{
public int Count
{
get
{
lock(this.tasks)
{
return this.tasks.Count;
}
}
}
private readonly Queue tasks = new Queue();
private readonly AutoResetEvent taskWaitHandle = new AutoResetEvent(false);
private bool isFinishing = false;
private bool isFinished = false;
public void Enqueue(Task task)
{
Log.Trace("Entering Enqueue, lock...");
lock(this.tasks)
{
Log.Trace("Adding task, current count = {0}...", Count);
this.tasks.Enqueue(task);
if (Count == 1)
{
Log.Trace("Count = 1, so setting the wait handle...");
this.taskWaitHandle.Set();
}
}
Log.Trace("Exiting enqueue...");
}
public Task Dequeue()
{
Log.Trace("Entering Dequeue...");
if (Count == 0)
{
if (this.isFinishing)
{
Log.Trace("Finishing (before waiting) - isCompleted set, returning empty task.");
this.isFinished = true;
return new Task();
}
Log.Trace("Count = 0, lets wait for a task...");
this.taskWaitHandle.WaitOne();
Log.Trace("Wait handle let us through, Count = {0}, IsFinishing = {1}, Returned = {2}", Count, this.isFinishing);
if(this.isFinishing)
{
Log.Trace("Finishing - isCompleted set, returning empty task.");
this.isFinished = true;
return new Task();
}
}
Log.Trace("Entering task lock...");
lock(this.tasks)
{
Log.Trace("Entered task lock, about to dequeue next item, Count = {0}", Count);
return this.tasks.Dequeue();
}
}
public void Finish()
{
Log.Trace("Setting TaskQueue state to isFinishing = true and setting wait handle...");
this.isFinishing = true;
if (Count == 0)
{
this.taskWaitHandle.Set();
}
}
public IEnumerator GetEnumerator()
{
while(true)
{
Task t = Dequeue();
if(this.isFinished)
{
yield break;
}
yield return t;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
正如您所看到的,我正在使用 AutoResetEvent 对象来确保工作线程不会过早退出,即在获取任何任务之前 .
简而言之:
主线程通过Enqeueue任务到TaskQueue为线程分配任务
主线程通过调用TaskQueue的Finish()方法通知线程不再执行任务
工作线程通过调用TaskQueue的Dequeue()方法检索分配给它的下一个任务
问题是 Dequeue ()方法经常 throws an InvalidOperationException ,说Queue是空的 . 正如您所看到的,我添加了一些日志记录,事实证明,AutoResetEvent不会阻止Dequeue(),即使没有调用其Set()方法 .
据我了解,调用AutoResetEvent.Set()将允许等待线程继续(之前调用AutoResetEvent.WaitOne()),然后自动调用AutoResetEvent.Reset(),阻止下一个服务员 .
那有什么不对?我弄错了吗?我某处有错误吗?我现在坐在这上面3个小时,但我无法弄清楚出了什么问题 . 请帮我!
非常感谢你!