取消另一个类 C# 中的任务

Posted

技术标签:

【中文标题】取消另一个类 C# 中的任务【英文标题】:Cancel task in another class C# 【发布时间】:2021-03-08 05:19:44 【问题描述】:

我对 C# 中的 cancelTokenSource 有问题

public class Building 
    public CancellationTokenSource BuildTokenSource;

    public void StartBuilt()
    
        BuildTokenSource = new CancellationTokenSource();

        buildingService.buildTask = Task.Run(async () =>
        
            await clock.Delay(BUILT_TIME);
        , BuildTokenSource.Token);
    

    public void CancelBuilt()
    
        if (BuildTokenSource != null)
        
            BuildTokenSource.Cancel();
        
    

在另一个类中想要检测任务是否像这样取消但它不起作用。从未触发过的 catch 异常

public async Task<Building> GetBuildingOfUserTask()
    
        double remainingTime = unitService.GetRemainingTime();

        if (remainingTime <= 2000 && remainingTime > 0.0)
        
            Building building = GetBuilding();
            CancellationToken cancellation = building.BuildTokenSource.Token;
            try
            
                await buildTask;
            
            catch (OperationCanceledException) when (cancellation.IsCancellationRequested)
            
                return GetBuildingOfUser();
            
        
        return GetBuildingOfUser();
    

有人知道为什么这不起作用,在这种情况下有解决方案吗?

【问题讨论】:

“它不起作用” 什么不起作用?编译器错误?例外?行为与预期不同? 任务没有取消。永远不会触发 catch 异常 相关:Using CancellationToken for timeout in Task.Run does not work 【参考方案1】:

clock.Delay(BUILT_TIME) 是否有接受CancellationToken 的重载?如果是这样,请使用它。

问题是如果代码在您取消时已经在等待clock.Delay(BUILT_TIME)clock.Delay 将不知道它需要抛出异常。

【讨论】:

【参考方案2】:

我没有看到任何地方调用CancelBuilt() + 你必须调用BuildTokenSource.Token.ThrowIfCancellationRequested() 才能引发OperationCanceledException 异常

token.Cancel() 应该在使用 cancelationToken 的异步方法之外调用。同样消费异步方法必须调用(通常在每一步)Token.ThrowIfCancellationRequested();

【讨论】:

【参考方案3】:

我相信使用调解器 (Mediator design pattern) 会更合适。这实际上是一个 pub-sub 模型,它将在取消时发布一个事件并通知所有订阅者。

第一个类将中介实例作为引用(只读字段、属性)来发布取消事件,另一个类应该有相同的实例作为引用,以便在事件实际发生时得到通知。您应该注意的另一点是,当包含“GetBuildingOfUserTask”方法的类的实例被销毁时应该取消订阅。

你怎么看?

【讨论】:

【参考方案4】:

我同意HadascokJ的回答,我想带来更多的光明。

您有一个主要任务从 Task buildingService.buildTask 开始,它的从属任务从 await clock.Delay(BUILT_TIME); 开始

第一个任务管理CancellationToken,但从属的不是。为了更好地使用Task Task.Delay(int millisecondsDelay, CancellationToken cancellationToken); 替换您的clock.Delay(BUILT_TIME),当然还要提供CancelationToken。您会看到,在这种情况下,从属任务将被取消。同时调用void CancellationToken.CancelAfter(int millisecondsDelay)

由于您没有为从属任务提供CancellationToken,因此主任务已启动,主任务及其从属任务都不会被取消。

另一方面,要在从属任务执行时取消,向从属任务提供一些逻辑来管理CancelationToken到其相应的方法中,并在必要时调用CancelationToken.ThrowIfCancellationRequested(),这会抛出OperationCanceledException .

至少,尝试将长任务分成几个小任务。 我用来管理异步。任务,必须按顺序运行到能够观察这些TaskStatus 的任务队列中。为了解决这个问题,如果你需要,我在 github 上有一个实现。我叫它FifoTaskQueue

【讨论】:

以上是关于取消另一个类 C# 中的任务的主要内容,如果未能解决你的问题,请参考以下文章

使用另一个类c#中的类

如何在c#中访问另一个类中的一个类的私有函数?

如何从 C# 中的另一个私有类调用私有类变量

如何使静态类中的所有方法在c#中的另一个静态类中可用

WP8.1 如何在 C# 中取消后台音频任务

仍然可以从 C# 中的另一个类访问私有变量