用于保存最大运行任务数的集合

时间:2016-12-25 17:04:44

标签: c# multithreading

我需要一个收集/包(某物)来保存一些运行Task个对象的最大数量。向集合添加新的运行Task应该阻止调用线程(有许多线程尝试添加任务,因此它应该是线程安全的),直到有一个可用的插槽来添加新的任务,如果最大值达到了正在运行的任务数。这是我到目前为止所做的工作正常。

public class ConcurrentTaskLimiter
{
    public int MaxWorkingTasks { get; }
    private readonly Task[] _tasks;
    private readonly bool[] _finished;

    public ConcurrentTaskLimiter(int maxWorkingTasks)
    {
        MaxWorkingTasks = maxWorkingTasks;
        if ((1 <= maxWorkingTasks) == false)
            throw new ArgumentOutOfRangeException(nameof(maxWorkingTasks), maxWorkingTasks, "Must be >= 1");
        _tasks = new Task[maxWorkingTasks];
        _finished = new bool[maxWorkingTasks];

        for (int i = 0; i < MaxWorkingTasks; i++)
        {
            _tasks[i] = Task.FromResult(0); // use this as finished tasks
            _finished[i] = true;
        }
    }

    public void BlockAdd(Task t)
    {
        if (t == null)
            throw new ArgumentNullException(nameof(t));

        if (t.Status == TaskStatus.Canceled
            || t.Status == TaskStatus.Faulted
            || t.Status == TaskStatus.RanToCompletion)
            return;

        lock (this)
        {
            int i;
            while (true)
            {
                for (i = 0; i < MaxWorkingTasks; i++)
                {
                    if (_finished[i])
                    {
                        _tasks[i] = t;
                        _finished[i] = false;
                        return;
                    }
                }
                i = Task.WaitAny(_tasks);
                _finished[i] = true;
            }
        }
    }
}

您是否发现此代码有任何问题?或者是否有一些内置类可以处理这种任务:)?

3 个答案:

答案 0 :(得分:3)

.NET有一个名为BlockingCollection<T>的类,可以大大简化您的任务。

阻止收集的实例可以使用其可以容纳的任务数量的上限进行初始化。执行此操作时,对Add的调用将在添加任务时阻止集合超出容量。

答案 1 :(得分:0)

使用SynchronizedCollection<T>它将为您提供开箱即用的线程安全性。或者使用System.Collections.Concurrent中的众多馆藏之一。我会使用System.Collections.Concurrent中的一个,因为它们更新,更有效。

答案 2 :(得分:0)

我会提出更优雅的解决方案。

    public class TasksPool
    {
        public int MaxSize { get; private set; }

        public TasksPool(int maxSize)
        {
            if (maxSize < 1) throw new IndexOutOfRangeException("Should be 1 or more");
            MaxSize = maxSize;
            _semaphore = new SemaphoreSlim(maxSize-1);
        }

        private readonly SemaphoreSlim _semaphore;

        public void Add(Task t, CancellationToken token)
        {
            _semaphore.Wait(token);

            if (token.IsCancellationRequested) return;
            t.ContinueWith(q => _semaphore.Release(), token);
        }
    }