我无法控制API中的以下方法。
public void Start(Action OnReady);
总的来说,我对回调很满意,但有时您会触发回调等等。因此,我想将其包装到异步方法中,也许还包括取消操作的可能性。像这样:
await Start(cancellationToken);
这是我想出的:
public Task Start(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
cancellationToken.ThrowIfCancellationRequested();
var readyWait = new AutoResetEvent(false);
cancellationToken.Register(() => readyWait?.Set());
Start(() => { readyWait.Set(); }); //this is the API method
readyWait.WaitOne();
readyWait.Dispose();
readyWait = null;
if(cancellationToken.IsCancellationRequested)
{
APIAbort(); //stop the API Start method from continuing
cancellationToken.ThrowIfCancellationRequested();
}
}, cancellationToken);
}
我认为仍有改进的余地,但我想到的一件事是,在这种情况下,这种方法是什么?
readyWait.WaitOne();
我想编写一个异步方法以不阻塞任何线程,但这正是WaitOne所做的。当然,它不会因为任务而阻塞调用线程,但是任务是否拥有自己的线程?如果只阻止任务,那很好,但是我不想阻止其他地方可能正在使用的线程。
答案 0 :(得分:3)
public Task StartAsync(CancellationToken c)
{
var cs = new TaskCompletionSource<bool>();
c.Register(() => { Abort(); cs.SetCanceled(); } );
Start(() => { cs.SetResult(true); });
return cs.Task;
}
using (var ct = new CancellationTokenSource(1000))
{
try
{
await StartAsync(ct.Token);
MessageBox.Show("Completed");
}
catch (TaskCanceledException)
{
MessageBox.Show("Cancelled");
}
}
没有必要使用取消令牌,因为它可以触发的唯一点是在调用方法之后立即完成之前,这是竞争条件。 >
答案 1 :(得分:0)
这个问题出奇的棘手。案例很多,每个案例的处理并不总是很明显。
显而易见:
Start
方法可能需要很长时间才能完成。 APIAbort
方法也是如此。CancellationToken
方法之前,之中或之后甚至在Start
回调调用之后取消OnReady
。APIAbort
回调调用之前或期间或之后,不应调用Start
。OnReady
方法之前发生的,也应调用APIAbort
。不明显:
Start
是否应在Task
调用之前或之后被取消?
APIAbort
引发异常怎么办?如果返回的APIAbort
已被取消,如何传播此异常?Task
抛出Start
异常怎么办?这是错误还是取消?以下实现在调用OperationCanceledException
方法之前先取消Task
,抑制在APIAbort
期间可能发生的异常,并处理
APIAbort
期间OperationCanceledException
被取消。
Start
设置选项public Task StartAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
return Task.FromCanceled(cancellationToken);
var tcs = new TaskCompletionSource<bool>();
var cancellationRegistration = cancellationToken.Register(() =>
tcs.TrySetCanceled(cancellationToken));
var fireAndForget = Task.Run(() =>
{
if (cancellationToken.IsCancellationRequested) return;
try
{
Start(() =>
{
cancellationRegistration.Dispose(); // Unregister
tcs.TrySetResult(true);
});
}
catch (OperationCanceledException)
{
tcs.TrySetCanceled();
return;
}
catch (Exception ex)
{
tcs.TrySetException(ex);
return;
}
// At this point Start is completed succesfully. Calling APIAbort is allowed.
var continuation = tcs.Task.ContinueWith(_ =>
{
try
{
APIAbort();
}
catch { } // Suppressed
}, default, TaskContinuationOptions.OnlyOnCanceled
| TaskContinuationOptions.RunContinuationsAsynchronously,
TaskScheduler.Default);
}, cancellationToken);
return tcs.Task;
}
的原因是为了避免与TaskContinuationOptions.RunContinuationsAsynchronously
之后的代码同步(在同一线程中)运行APIAbort
的可能性。最初我使用await StartAsync()
-async
作为延续机制时遇到了this problem。
答案 2 :(得分:0)
我想将其包装到异步方法中,也许还可以取消操作。
我建议将它们分开。原因是您的“取消”实际上并未取消expired_at
。它只取消Start
的 wait 。因此,在此级别取消会产生误导。
您可以使用与pattern for wrapping an event into a Task
类似的方法将委托回调包装到Start
中:
Task
现在,您可以致电public static Task StartAsync(this ApiObject self)
{
var tcs = new TaskCompletionSource<object>();
self.Start(() => tcs.SetResult(null));
return tcs.Task;
}
并取回StartAsync
,如果需要,您可以选择不继续等待:
Task