C# 重入时取消异步方法的模式

C# 重入时取消异步方法的模式,c#,asynchronous,cancellation-token,reentrancy,C#,Asynchronous,Cancellation Token,Reentrancy,我想取消重入时的异步函数,这样工作就不会堆积起来,避免了不必要的工作。 e、 我的文件扫描可能需要8秒钟,但是当我在UI中更改文件夹时,旧功能应该被取消。 我看到了带有CancellationToken的示例,但在我看来似乎太多代码了 我的方法是这样的,它似乎是有效的,但它给代码增加了很多混乱。 也许我也错过了捕捉TaskCanceledException,它将添加更多的代码 private CancellationTokenSource scanFilesState; private ILis

我想取消重入时的异步函数,这样工作就不会堆积起来,避免了不必要的工作。 e、 我的文件扫描可能需要8秒钟,但是当我在UI中更改文件夹时,旧功能应该被取消。 我看到了带有
CancellationToken
的示例,但在我看来似乎太多代码了

我的方法是这样的,它似乎是有效的,但它给代码增加了很多混乱。 也许我也错过了捕捉
TaskCanceledException
,它将添加更多的代码

private CancellationTokenSource scanFilesState;
private IList<FileInfo> files;

private async Task ScanFilesAsync(string path)
{
    // stop old running
    this.scanFilesState?.Cancel();
    this.scanFilesState?.Dispose();

    this.scanFilesState = new CancellationTokenSource();

    this.files = await FileHandler.ScanFilesAsync(path, this.scanFilesState.Token);

    this.scanFilesState?.Dispose();
    this.scanFilesState = null;
}
私有取消令牌源扫描文件状态;
私有IList文件;
专用异步任务ScanFileAsync(字符串路径)
{
//别老跑了
此.scanFilesState?.Cancel();
此.scanfilestate?.Dispose();
this.scanFilesState=新的CancellationTokenSource();
this.files=await FileHandler.scanfileasync(路径为this.scanfilestate.Token);
此.scanfilestate?.Dispose();
this.scanFilesState=null;
}
有没有更短或更好的方法


是否有一个模式来包装此代码?

我似乎不需要在, 并提出了这种方法,并将CancellationTokenSource包装为安全处理

public class SafeCancellationTokenSource : IDisposable
    {
        private CancellationTokenSource state = new CancellationTokenSource();

        public CancellationTokenSource State => state;

        public CancellationToken Token => State.Token;

        public bool IsCancellationRequested => State.IsCancellationRequested;

        public void Cancel()
        {           
            this.state?.Cancel();
            this.state?.Dispose();
            this.state = new CancellationTokenSource();
        }

        public void Dispose()
        {
            this.state?.Dispose();
            this.state = null;
        }
    }
代码现在看起来像这样

private SafeCancellationTokenSource scanFilesState =  new SafeCancellationTokenSource();
private IList<FileInfo> files;

private async Task ScanFilesAsync(string path)
{   
    this.scanFilesState.Cancel();
    this.files = await FileHandler.ScanFilesAsync(path, this.scanFilesState.Token);
}
private SafeCancellationTokenSource scanfilestate=new SafeCancellationTokenSource();
私有IList文件;
专用异步任务ScanFileAsync(字符串路径)
{   
这个.scanFilesState.Cancel();
this.files=await FileHandler.scanfileasync(路径为this.scanfilestate.Token);
}
编辑:
添加到处理()的调用再次(

),您可以考虑将这种混乱的功能封装在<代码> CabelababeReals< /Cuff>类中,具有<代码>任务Runasyc(Func Action)公共API。有一个实现,由于对线程安全的额外要求,它有相当多的代码。在你的例子中,我想你所有的代码都是在UI线程上运行的,所以实现可以简单得多。好的,谢谢,这似乎是我所需要的。我很可能在UI线程上运行,但不确定。为什么是评论而不是真实的回复帖子,这样我就可以将其标记为答案?在GUI应用程序中,
wait
之后的延续在UI线程上运行(除非您明确地将其配置为其他方式),因此,您不必担心一个线程取消
CancellationTokenSource
,而另一个线程取消它的情况。无论在
FileHandler.scanfileasync中发生什么,关于CTS的处理都无关紧要。因此,花一些时间编写一个更简单(单线程)的实现是有意义的,您可以随意轻松地进行自定义。它本身不值得回答::-)取消是一种协作功能,它不会取消已启动的任务,因为这会导致系统不稳定。一旦任务开始,那么就没有停止,你不能忽略它是的,我删除了Dispose()调用,因为害怕ObjectDisposedException,并使用了Disposable列表,就像链接线程中建议的那样。谢谢,我会删除它。好的,我删除了我的评论。顺便说一句,当您完全控制
CancellationTokenSource
时,不要害怕
ObjectDisposedException
。仅持有令牌引用的客户端(
CancellationToken
)不能执行任何可能导致抛出
ObjectDisposedException
的操作。你只需要担心你自己的代码。例如,访问已处置的
CancellationTokenSource
抛出的
Token
属性。您还应注意以下备注:“此类型实现了
IDisposable
接口。当您使用完该类型的实例后,应直接或间接处置它。[…]否则,在垃圾收集器调用对象的
Finalize
方法之前,它正在使用的资源将不会被释放。根据您评论中的链接,它说,调用Cancel()时不需要Dispose(),除非ist是LinkedCancellationTokenSource。[.所以我应该再次添加对Dispose()的调用?我建议将其放回原位。处理丢弃的源代码是正确的做法。因为在这之后立即用新的
CancellationTokenSource
替换引用,所以我看不到以后访问旧的已处置源代码并抛出异常的任何可能性。