【问题标题】:How to cancel one particular task out of N number of tasks?如何取消 N 个任务中的一个特定任务?
【发布时间】:2019-02-15 16:05:53
【问题描述】:

我有 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();                                               
                }
            });
}

【问题讨论】:

  • 为什么将取消令牌存储在myConcurrentDictionary 中,然后在DataService.serviceCancellationTokenMap 中搜索?为什么不在Start 方法中为serviceCancelSource 指定类型?它是课堂上的一个领域吗?一般来说 - 无法使用您提供的代码重现问题。
  • @SergeyShevchenko 我的错。我修改了原始代码以将示例放在这里,并遗漏了一些地方。我已经编辑了代码。 _serviceCancelSource 是正确的,它是类中的一个字段。
  • 不确定我是否正确阅读了您的代码,但如果您共享令牌源,那么它将取消所有令牌,因此所有从该源接收令牌的任务。您不需要共享令牌源。但同样,我从阅读您的代码中并不确定是否足以回答这个问题。
  • @Kit 每次我将新的令牌源添加到每个服务的字典中。所以它不被共享。
  • @Raj 我想我的困惑是因为_serviceCancelSourceserviceCancelSource。当您的字典中存储有源时,为什么需要前一个字段?

标签: c# task-parallel-library


【解决方案1】:

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

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

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

因此,并发字典最终会为所有“Service.ids”拥有相同的 CancellationTokenSource 实例,这可能是取消一项任务会取消所有其他任务的原因。

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

【讨论】:

  • 我删除了该字段,添加了局部变量,现在我可以从字典中取消任何特定任务。感谢 Praveen 拯救了我的一天。由于我是新手,我无法将其作为答案结束。
猜你喜欢
  • 1970-01-01
  • 2020-04-15
  • 1970-01-01
  • 1970-01-01
  • 2022-07-11
  • 1970-01-01
  • 1970-01-01
  • 2013-09-30
  • 1970-01-01
相关资源
最近更新 更多