1)你应该从EndExecute
方法中重新抛出异常。
2)我建议您创建自己的基础类型。我写了一个名为AsyncTaskCodeActivity<T>
的基础类型如下:
public abstract class AsyncTaskCodeActivity<T> : AsyncCodeActivity<T>
{
protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
{
var task = ExecuteAsync(context);
var tcs = new TaskCompletionSource<T>(state);
task.ContinueWith(t =>
{
if (t.IsFaulted)
tcs.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCanceled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(t.Result);
if (callback != null)
callback(tcs.Task);
});
return tcs.Task;
}
protected sealed override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
{
var task = (Task<T>)result;
try
{
return task.Result;
}
catch (AggregateException ex)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
throw;
}
}
protected abstract Task<T> ExecuteAsync(AsyncCodeActivityContext context);
}
如果您使用我的
AsyncEx库,这个包装器会变得更加简单:
public abstract class AsyncTaskCodeActivity<T> : AsyncCodeActivity<T>
{
protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
{
var task = ExecuteAsync(context);
return AsyncFactory<T>.ToBegin(task, callback, state);
}
protected sealed override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
{
return AsyncFactory<T>.ToEnd(result);
}
protected abstract Task<T> ExecuteAsync(AsyncCodeActivityContext context);
}
一旦你有了基本类型,你可以定义自己的派生类型。这里有一个使用 async
/await
的例子:
public sealed class MyActivity : AsyncTaskCodeActivity<int>
{
protected override async Task<int> ExecuteAsync(AsyncCodeActivityContext context)
{
await Task.Delay(100);
return 13;
}
}
这里有一个将CPU绑定的工作计划到线程池的示例(类似于您当前使用的模板):
public sealed class MyCpuActivity : AsyncTaskCodeActivity<int>
{
protected override Task<int> ExecuteAsync(AsyncCodeActivityContext context)
{
return Task.Run(() => 13);
}
}
评论更新:这里有一个使用取消操作的示例。我不能百分之百确定它是否正确,因为取消本身是异步的,并且AsyncCodeActivity<T>.Cancel
的语义未被充分记录(即,Cancel
是否应该等待活动在已取消状态下完成?在调用Cancel
后成功完成活动是否可接受?)。
public abstract class AsyncTaskCodeActivity<T> : AsyncCodeActivity<T>
{
protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
{
var cts = new CancellationTokenSource();
context.UserState = cts;
var task = ExecuteAsync(context, cts.Token);
return AsyncFactory<T>.ToBegin(task, callback, state);
}
protected sealed override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
{
try
{
return AsyncFactory<T>.ToEnd(result);
}
catch (OperationCanceledException)
{
if (context.IsCancellationRequested)
context.MarkCanceled();
else
throw;
return default(T);
}
}
protected override void Cancel(AsyncCodeActivityContext context)
{
var cts = (CancellationTokenSource)context.UserState;
cts.Cancel();
}
protected abstract Task<T> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken);
}
BeginExecute
中创建一个CancellationTokenSource
(并将其保存在上下文中),将令牌传递给ExecuteAsync
。然后重写Cancel
方法以从上下文中获取CancellationTokenSource
,取消它,并调用MarkCanceled
方法。 - Stephen Clearyawait
之后,在ExecuteAsync
方法中尝试使用context
时,我遇到了异常信息:“只有在传递给函数的范围内才能访问ActivityContext。” - Kris Ivanovasync void
。如果这不是问题,请将最小重现作为问题发布。 - Stephen Cleary