c# - 串行任务执行器;这个线程安全吗?

标签 c# multithreading synchronization thread-safety task

我创建了一个类,允许异步顺序执行任务,使用 ThreadPool 作为执行方式。这个想法是我将有多个实例在后台运行串行任务,但我不想为每个实例都有一个单独的专用线程。我想检查的是这个类是否真的是线程安全的。它相当简短,所以我想我应该由这里的专家运行它,以防我遗漏一些明显的东西。我省略了一些针对不同 Action 类型的便捷重载。

/// <summary>
/// This class wraps ThreadPool.QueueUserWorkItem, but providing guaranteed ordering of queued tasks for this instance.
/// Only one task in the queue will execute at a time, with the order of execution matching the order of addition.
/// This is designed as a lighter-weight alternative to using a dedicated Thread for processing of sequential tasks.
/// </summary>
public sealed class SerialAsyncTasker
{
    private readonly Queue<Action> mTasks = new Queue<Action>();
    private bool mTaskExecuting;

    /// <summary>
    /// Queue a new task for asynchronous execution on the thread pool.
    /// </summary>
    /// <param name="task">Task to execute</param>
    public void QueueTask(Action task)
    {
        if (task == null) throw new ArgumentNullException("task");

        lock (mTasks)
        {
            bool isFirstTask = (mTasks.Count == 0);
            mTasks.Enqueue(task);

            //Only start executing the task if this is the first task
            //Additional tasks will be executed normally as part of sequencing
            if (isFirstTask && !mTaskExecuting)
                RunNextTask();
        }
    }

    /// <summary>
    /// Clear all queued tasks.  Any task currently executing will continue to execute.
    /// </summary>
    public void Clear()
    {
        lock (mTasks)
        {
            mTasks.Clear();
        }
    }

    /// <summary>
    /// Wait until all currently queued tasks have completed executing.
    /// If no tasks are queued, this method will return immediately.
    /// This method does not prevent the race condition of a second thread 
    /// queueing a task while one thread is entering the wait;
    /// if this is required, it must be synchronized externally.
    /// </summary>
    public void WaitUntilAllComplete()
    {
        lock (mTasks)
        {
            while (mTasks.Count > 0 || mTaskExecuting)
                Monitor.Wait(mTasks);
        }
    }

    private void RunTask(Object state)
    {
        var task = (Action)state;
        task();
        mTaskExecuting = false;
        RunNextTask();
    }

    private void RunNextTask()
    {
        lock (mTasks)
        {
            if (mTasks.Count > 0)
            {
                mTaskExecuting = true;
                var task = mTasks.Dequeue();
                ThreadPool.QueueUserWorkItem(RunTask, task);
            }
            else
            {
                //If anybody is waiting for tasks to be complete, let them know
                Monitor.PulseAll(mTasks);
            }
        }
    }
}

更新:我修改了代码以修复 Simon 友善指出的主要错误。这现在通过了单元测试,但我仍然欢迎观察。

最佳答案

不要这样做。 (或者至少避免构建自己的东西。)

使用 System.Threading.Tasks东西(.NET 4.0 中的新功能)。创建您的 Task [](大小取决于您想要的并行任务的数量)并让他们从 BlockingCollection 中读取工作项在等待 CancellationToken 时.您的 WaitForAll 实现将触发您的 token ,并调用 Task.WaitAll(Task[])它将阻塞,直到您完成所有任务。

关于c# - 串行任务执行器;这个线程安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3670914/

相关文章:

c# - LINQ to SQL 是否可以使用其他语言?

c# - 将 C++ 二维固定长度 char 数组编码(marshal)为结构成员

synchronization - CouchDB数据同步

java - 单例、线程安全和互斥体

java - 线程的意外行为

java - 具有同步和非同步块(synchronized block)执行的程序

c# - 在一个时间跨度内循环

单击事件的 C# 表单应用程序

c# - 锁定原始类型

python - Python 中的线程需要更长的时间而不是使其更快?