如何从N个任务中取消一个特定任务?

时间:2019-02-15 16:05:53

标签: c# task-parallel-library

我有N个具有其自己的CancellationTokenSource的任务。为了跟踪每个任务及其CancellationTokenSource,我使用了ConcurrentDictionary。这样,每当我需要取消特定任务时,我都会从ConcurrentDictionary获取该任务的CancellationTokenSource并将其取消。

问题是,每当我取消一项任务时,所有其他任务也会被取消。有什么我想念的吗?下面的示例代码。

CancellationTokenSource _serviceCancelSource;
public async Task Start()
{    
    var services = _serviceRepository.GetAll();
    await Task.Run(() =>
    {
        Parallel.ForEach(services, x => Task.Run(() => Start(x)));
    });           
}

public async Task Start(Service service)
{
    _serviceCancelSource = new CancellationTokenSource();
    myConcurrentDictionary.AddOrUpdate(service.Id, _serviceCancelSource, (key, oldValue) => _serviceCancelSource));
    var manager = ServiceManagerFactory.Create(service);
    Action serviceAction = () => manager.InitializeTaskAsync();
    await Task.Run(() =>  PeriodicTaskFactory.Start(serviceAction, intervalInMilliseconds: service.PollTime, cancelToken: _serviceCancelSource.Token, serviceName: service.ServiceName));
}

public async Task CancelTask()
{    
    await Task.Run(() =>
            {
                var serviceId = GetChangedService();
                CancellationTokenSource cancelSource;
                if (myConcurrentDictionary.TryGetValue(serviceId, out cancelSource))
                {
                    cancelSource.Cancel();                                               
                }
            });
}

1 个答案:

答案 0 :(得分:0)

我在代码中看到的一个问题是共享变量“ _serviceCancelSource”。

上面的代码中的Parallel.Foreach将在多个线程上并行调用“ Start(service)”方法,因此语句“ _serviceCancelSource = new CancellationTokenSource();”几乎在所有线程上同时执行。 请记住,“ _ serviceCancelSource”是一个在所有线程之间共享的变量。

当控件到达“ myConcurrentDictionary.AddOrUpdate()”(它可能具有某种线程安全锁定机制)时,共享变量“ _serviceCancelSource”将具有最后完成的线程的值。

结果,并发字典最终对所有“ Service.ids”都具有相同的CancellationTokenSource实例,这可能就是取消一个任务会取消所有其他任务的原因。

如果您要将新的取消令牌分配给局部变量,然后将其添加到并发字典中,请告诉我们结果。