AutoResetEvent未正确阻止

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对象来确保工作线程不会过早退出,即在获取任何任务之前.

简而言之:

  • 主线程通过Enqeueue将任务分配给线程 - 将任务分配给其TaskQueue
  • 主线程通过调用TaskQueue的Finish()方法通知线程不再执行任务
  • 工作线程通过调用TaskQueue的Dequeue()方法检索分配给它的下一个任务

问题是Dequeue()方法经常抛出一个InvalidOperationException,说Queue是空的.正如您所看到的,我添加了一些日志记录,事实证明,AutoResetEvent不会阻止Dequeue(),即使没有调用其Set()方法.

据我了解,调用AutoResetEvent.Set()将允许等待线程继续(之前调用AutoResetEvent.WaitOne()),然后自动调用AutoResetEvent.Reset(),阻止下一个服务员.

那有什么不对?我弄错了吗?我某处有错误吗?我现在坐在这上面3个小时,但我无法弄清楚出了什么问题.请帮我!

非常感谢你!

Rem*_*anu 6

您的出列代码不正确.你在锁定下检查计数,然后通过裤子的接缝飞行,然后你希望任务有一些东西.释放锁定时,您无法保留假设:).您的计数检查和任务.取消必须在锁定下进行:

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)

  • 该死的,那太苛刻了. (3认同)