C# 取消并等待异步任务方法
试图弄明白这一点,但找不到任何确定的东西 假设我有一个异步工作的类,可以动态启动或停止:C# 取消并等待异步任务方法,c#,asynchronous,async-await,task,C#,Asynchronous,Async Await,Task,试图弄明白这一点,但找不到任何确定的东西 假设我有一个异步工作的类,可以动态启动或停止: class ModelObject { private CancellationTokenSource cts; private CancellationToken ct; private Progress<int> progress; private async Task DoSomething(IProgress<int> progres
class ModelObject
{
private CancellationTokenSource cts;
private CancellationToken ct;
private Progress<int> progress;
private async Task DoSomething(IProgress<int> progress)
{
...
while (DoingSomething)
{
if (ct.IsCancellationRequested)
{
await Task.Delay(10000); // Take our sweet time to cancel
// Cancel work and return
}
...
}
}
public async void StartAsync()
{
progress = new Progress<int>(...);
cts = new CancellationTokenSource();
ct = cts.Token;
await DoSomething(progress);
}
public void Stop()
{
ct.Cancel();
// HELP: await while DoSomething cancels
// perform cleanup
}
}
非常感谢您的帮助。我不确定StartAsync方法为什么是异步的,因为除非调用Stop,否则它将无限期地执行。我的意思是,如果不采取其他行动,它将不会在某个时间点返回。我觉得在这个类中不需要异步
但如果有必要,我已经起草了可能会有所帮助的代码
class ModelObject {
private CancellationTokenSource cts;
private CancellationToken ct;
private Progress<int> progress;
private Task doSomethingTask;
private async Task DoSomething(IProgress<int> progress){
...
while (DoingSomething)
{
if (ct.IsCancellationRequested)
{
// Cancel work and return
}
...
}
}
public async void StartAsync () {
progress = new Progress<int>(...);
cts = new CancellationTokenSource ();
ct = cts.Token;
doSomethingTask = DoSomething (progress);
await doSomethingTask;
}
public void Stop () {
cts.Cancel();
doSomethingTask.Wait();
}
}
虽然您可以等待DoSomething返回的任务,但也可以使用TaskCompletionSource
我倾向于向ModelObject类添加一个Completion属性。调用方可以选择在取消操作后等待或不等待完成。此外,我可能会对API的不当使用进行防范。在现有操作挂起时尝试启动新操作将导致异常
public Task Completion { get; private set; } = Task.CompletedTask;
public async void Start()
{
if (_cts != null) throw new InvalidOperationException("Operation is in progress.");
using (_cts = new CancellationTokenSource())
{
_ct = _cts.Token;
_progress = new Progress<int>(/*...*/);
this.Completion = DoSomethingAsync();
try
{
await this.Completion;
}
catch { } // Ignore
finally
{
_cts = null;
_ct = default;
_progress = null;
}
}
}
public void Stop()
{
_cts?.Cancel();
}
只是一个小提示:异步void是不好的。它应该是异步任务。A在这里会很好。很难说清你在清理什么,所以很难提供解决方案。@Enigmativity Hi做了一个小改动,希望这能有所帮助。清理,如删除正在处理的文件等。您阅读了该页面吗?您当然没有提供任何类似的解决方案。如果答案不建议使用。请稍候,这将是一个完整的解决方案…感谢您的详细解释。我在Stop方法中执行清理的原因是因为我希望能够改变清理,例如停止、取消、暂停或其他操作的不同清理。我的目标是让剂量测量任务只关注实际任务,而不是处理清理。另外,Start方法应该只与启动任务有关。@antikbd在我的示例中,Start是async void,因此从调用方的角度来看,它只不过是启动任务。它还向任务附加了一个延续,以便在任务完成时更新类的内部状态,这一事实对调用方是透明的。关于对不同风格的站点进行不同的清理,您可以对每个站点使用不同的CT,或者使用一些额外的状态,如:public void Stop1{{u stopType=1;_CTS?.Cancel;}。谢谢。是的,这是我目前的方法-使用多个CancellationToken。但是,我希望我不必检查多个令牌,因为可能会出现错误,我可以只使用一个令牌和多个方法。@antikbd有不同的方法,所有这些方法都试图改变内部状态,希望调用方不会在错误的时间、多次或不正确的顺序调用它们,这对我来说似乎更容易出错! private TaskCompletionSource<object> tcs; private async Task DoSomething(IProgress<int> progress){ ... while (DoingSomething) { if (ct.IsCancellationRequested) { // Cancel work and return } ... } tcs.SetResult(null); } public async Task Stop () { cts.Cancel(); await tcs.Task; }
public Task Completion { get; private set; } = Task.CompletedTask;
public async void Start()
{
if (_cts != null) throw new InvalidOperationException("Operation is in progress.");
using (_cts = new CancellationTokenSource())
{
_ct = _cts.Token;
_progress = new Progress<int>(/*...*/);
this.Completion = DoSomethingAsync();
try
{
await this.Completion;
}
catch { } // Ignore
finally
{
_cts = null;
_ct = default;
_progress = null;
}
}
}
public void Stop()
{
_cts?.Cancel();
}
private async Task DoSomethingAsync()
{
try
{
while (doingSomething)
{
// Do some work
_ct.ThrowIfCancellationRequested();
}
}
catch (OperationCanceledException)
{
await Task.Delay(10000); // Take our sweet time to cancel
throw; // Rethrow the exception
}
finally
{
// perform cleanup, whether successful, failed or canceled
}
}