【问题标题】:Add further tasks to an existing WaitAll将更多任务添加到现有的 WaitAll
【发布时间】:2016-04-18 00:21:12
【问题描述】:

假设我有一个Tasks 的集合,我要去WaitAll()。 假设,在它们全部完成之前,我想向该集合添加更多任务,并且我希望继续等待,直到 它们也全部完成。我可能会在结束之前添加更多任务,等等。

我可以使用 TPL 做到这一点吗?还是我将不得不手动滚动自己的线程管理? (糟糕!)

WaitAll() 作用于Task[],所以我不能只打开一个List<Task>,我称之为.ToArray(),因为那样等待就不会知道添加的任何新任务?

适用于WaitAny() 的类似问题。

【问题讨论】:

    标签: c# multithreading task-parallel-library


    【解决方案1】:

    这是WaitAll 的一种解决方案:

    假设您有以下列表来保存任务:

    List<Task> tasks = new List<Task>();
    

    您可以通过以下方式等待他们:

    while (true)
    {
        Task[] my_tasks;
    
        lock (tasks)
        {
            my_tasks = tasks.ToArray(); //take snapshot
            tasks.Clear(); //clear list
        }
    
        if (my_tasks.Length == 0)
            break;
    
        Task.WaitAll(my_tasks);
    }
    

    只需确保在将任务添加到列表时锁定列表,如下所示:

    lock (tasks)
    {
        tasks.Add(...
    }
    

    顺便说一句,您是否有理由同步等待任务而不是异步(您使用的是WaitAll 而不是WhenAll)?

    【讨论】:

    • 重新等待/何时,因为这是升级庞大遗留代码库核心的线程(替换基于 QueueUserWorkItem + ManualResetEvent 的信号量)并具有异步和/或回调炎传播通过整个代码库的其余部分将是站不住脚的。
    【解决方案2】:

    如果你想同步等待某个东西,然后再等待另一个东西,你需要取消原来的等待并开始一个新的等待。对一开始的内容进行原始等待,然后当您需要添加更多任务时,将它们添加到当前任务列表并发出取消信号以重新开始等待。

    public class MutableTaskWaiter
    {
        private List<Task> _tasks = new List<Task>();
        private CancellationTokenSource _cts;
    
        public IEnumerable<Task> Tasks
        {
            get
            {
                lock (_tasks)
                {
                    return _tasks.ToArray();
                }
            }
        }
    
        public void WaitAll(IEnumerable<Task> tasks)
        {
            WaitMoreTasks(tasks);
    
            do
            {
                try
                {
                    _cts = new CancellationTokenSource();
                    Task.WaitAll(_tasks.ToArray(), _cts.Token);
                }
                catch (OperationCanceledException)
                {
                    // start over and wait for new tasks
                }
            }
            while (_cts.IsCancellationRequested);
        }
    
    
        public void WaitAny(IEnumerable<Task> tasks)
        {
            WaitMoreTasks(tasks);
    
            do
            {
                try
                {
                    _cts = new CancellationTokenSource();
                    Task.WaitAny(_tasks.ToArray(), _cts.Token);
                }
                catch (OperationCanceledException)
                {
                    // start over and wait for new tasks
                }
            }
            while (_cts.IsCancellationRequested);
        }
    
    
        public void WaitMoreTasks(IEnumerable<Task> tasks)
        {
            lock (_tasks)
            {
                _tasks.AddRange(tasks);
                if (_cts != null)
                {
                    // signal the wait to restart with the updated task list
                    _cts.Cancel();
                }
            }
        }
    }
    

    当然,如果您长时间添加任务并且最初有一些短期任务,您仍然必须处理 WaitAll 场景中出现的竞争条件。例如如果我的初始任务列表在 5 秒后完成,我无法在 10 秒后将新任务添加到等待列表中,因为我已经完成了等待。

    【讨论】:

      猜你喜欢
      • 2014-03-30
      • 1970-01-01
      • 1970-01-01
      • 2020-10-02
      • 2014-09-27
      • 1970-01-01
      • 1970-01-01
      • 2015-07-08
      • 1970-01-01
      相关资源
      最近更新 更多