Shd*_*dNx 1 .net c# multithreading autoresetevent
我有一个线程,它创建可变数量的工作线程并在它们之间分配任务.这是通过向线程传递一个TaskQueue对象来解决的,您将在下面看到它的实现.
这些工作线程只是迭代它们给出的TaskQueue对象,执行每个任务.
private class TaskQueue : IEnumerable<Task>
{
public int Count
{
get
{
lock(this.tasks)
{
return this.tasks.Count;
}
}
}
private readonly Queue<Task> tasks = new Queue<Task>();
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<Task> GetEnumerator()
{
while(true)
{
Task t = Dequeue();
if(this.isFinished)
{
yield break;
}
yield return t;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,我正在使用AutoResetEvent对象来确保工作线程不会过早退出,即在获取任何任务之前.
简而言之:
问题是Dequeue()方法经常抛出一个InvalidOperationException,说Queue是空的.正如您所看到的,我添加了一些日志记录,事实证明,AutoResetEvent不会阻止Dequeue(),即使没有调用其Set()方法.
据我了解,调用AutoResetEvent.Set()将允许等待线程继续(之前调用AutoResetEvent.WaitOne()),然后自动调用AutoResetEvent.Reset(),阻止下一个服务员.
那有什么不对?我弄错了吗?我某处有错误吗?我现在坐在这上面3个小时,但我无法弄清楚出了什么问题.请帮我!
非常感谢你!
您的出列代码不正确.你在锁定下检查计数,然后通过裤子的接缝飞行,然后你希望任务有一些东西.释放锁定时,您无法保留假设:).您的计数检查和任务.取消必须在锁定下进行:
bool TryDequeue(out Tasks task)
{
task = null;
lock (this.tasks) {
if (0 < tasks.Count) {
task = tasks.Dequeue();
}
}
if (null == task) {
Log.Trace ("Queue was empty");
}
return null != task;
}
Run Code Online (Sandbox Code Playgroud)
您的Enqueue()代码同样充满了问题.您的Enqueue/Dequeue不能确保进度(即使队列中有项目,您也会将已排队的线程阻塞等待).你的签名Enqueue()是错的.总的来说你的帖子是非常糟糕的代码.坦率地说,我觉得你想要咀嚼的比你在这里咬的更多......哦,永远不要锁定.
我强烈建议您使用ConcurrentQueue.
如果您无权访问.Net 4.0,那么这是一个让您入门的实现:
public class ConcurrentQueue<T>:IEnumerable<T>
{
volatile bool fFinished = false;
ManualResetEvent eventAdded = new ManualResetEvent(false);
private Queue<T> queue = new Queue<T>();
private object syncRoot = new object();
public void SetFinished()
{
lock (syncRoot)
{
fFinished = true;
eventAdded.Set();
}
}
public void Enqueue(T t)
{
Debug.Assert (false == fFinished);
lock (syncRoot)
{
queue.Enqueue(t);
eventAdded.Set();
}
}
private bool Dequeue(out T t)
{
do
{
lock (syncRoot)
{
if (0 < queue.Count)
{
t = queue.Dequeue();
return true;
}
if (false == fFinished)
{
eventAdded.Reset ();
}
}
if (false == fFinished)
{
eventAdded.WaitOne();
}
else
{
break;
}
} while (true);
t = default(T);
return false;
}
public IEnumerator<T> GetEnumerator()
{
T t;
while (Dequeue(out t))
{
yield return t;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2909 次 |
| 最近记录: |