使用成功和故障处理程序在 csharp 中链接任务
Chaining Tasks in csharp with success and fault handler
编辑 请参阅我问题末尾的标题 "Problem" 以破解此问题。
来自我们可以链接承诺的 nodejs,在 C# 中,我看到异步任务几乎具有可比性。这是我的尝试。
编辑 - 我无法将超级级别的调用方方法标记为 async
,因为基于 dll 的库正在调用它
来电object
public void DoSomething(MyRequest request)
{
Delegate.Job1(request)
.ContinueWith(Delegate.Job2)
.ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(Result);
}
public void Result(Task<MyRequest> task)
{
MyRequest request = task.Result;
Console.Writeline(request.result1 + " " + request.result2);
}
public void Fault(Task<MyRequest> task)
{
MyRequest request = task.Result;
Console.Writeline(request.result);
}
代表Object
public async Task<MyRequest> Job1(MyRequest request)
{
var data = await remoteService.Service1Async();
request.result1 = data;
return request;
}
public async Task<MyRequest> Job2(Task<MyRequest> task)
{
var data = await remoteService.Service2Async();
request.result2 = data;
return request;
}
问题:
1) 编辑(已修复,链接到我的项目的 dll 丢失了它的链接 dll)Task.Result
(请求)在 Result
方法中为 null,另外 Status = Faulted
2) 故障处理是否正确?我希望只有在委托方法中发生异常时才会调用 Fault,否则应该跳过。
2-b) 另一种选择是在 Result
函数中检查(删除 Fault
函数)如果 Task.status = RanTocompletion
并在那里分支以判断成功或错误
回答后编辑
我有一个问题,如果我不能让我的控制器异步怎么办。
控制器
public void ICannotBeAsync()
{
try
{
var r = await caller.DoSomething(request); // though I can use ContinueWith here, ???
}
catch(Exception e)
{
//exception handling
}
}
来电
public async Task DoSomethingAsync(MyRequest request)
{
request.result1 = await delegateInstance.Job1(request);
request.result2 = await delegateInstance.Job2(request);
Console.Writeline(request.result1 + " " + request.result2);
return result;
}
编辑 2 - 基于 VMAtm 编辑,请查看 OnlyOnFaulted(故障)选项。
Delegate.Job1(request)
.ContinueWith(_ => Delegate.Job2(request), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(() => {request.result = Task.Exception; Fault(request);}, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(Result, TaskContinuationOptions.OnlyOnRanToCompletion);
问题 -
测试一下,下面的实际代码,Result
或 Fault
的 none 被调用,尽管方法 GetCustomersAsync
返回成功。我的理解是一切都在 Fault
处停止,因为它仅在 Fault
上被标记为 运行,执行在那里停止并且未调用 Result
处理程序。
Customer.GetCustomersAsync(request)
.ContinueWith(_ => { Debug.WriteLine("not executing"); Fault(request); }, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(_ => { Debug.WriteLine("not executing either"); Result(request); }, TaskContinuationOptions.OnlyOnRanToCompletion);
编辑 3 基于 Evk 的回答。
Task<Request> task = Customer.GetCustomersAsync(request);
task.ContinueWith(_ => Job2Async(request), TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(_ => Job3Async(request), TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(_ => Result(request), TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t => { request.Result = t.Exception; Fault(request); }, TaskContinuationOptions.OnlyOnFaulted);
此代码存在多个问题:
Delegate.Job1(request)
.ContinueWith(Delegate.Job2)
.ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(Result);
首先,即使 Delegate.Job1
失败,您仍将继续执行 Delegate.Job2
。所以这里需要一个 OnlyOnRanToCompletion
值。与 Result
延续类似,您在所有情况下都在继续,因此出现错误的任务仍会通过链,并且如您所见,处于 Faulted
状态 null
结果。
因此,如果您不能在该级别 await
上使用,您的代码可能是这样的(另外,正如@Evk 所述,您必须将异常处理添加到所有代码中,这真的很难看):
Delegate.Job1(request)
.ContinueWith(Delegate.Job2, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(Result, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted);
但是,您仍然可以选择在您的方法中使用 await
关键字,然后使用 lambda 同步地 运行 它,如果您可以选择:
public async Task DoSomethingAsync(MyRequest request)
{
try
{
request.result1 = await delegateInstance.Job1(request);
request.result2 = await delegateInstance.Job2(request);
Console.Writeline(request.result1 + " " + request.result2);
return result;
}
catch(Exception e)
{
}
}
public void ICannotBeAsync()
{
var task = Task.Run(() => caller.DoSomethingAsync(request);
// calling the .Result property will block current thread
Console.WriteLine(task.Result);
}
异常处理可以在任一级别上完成,因此由您决定在何处引入它。如果在执行过程中出现问题,Result
property will raise an AggregateException
as a wrapper to inner exceptions happened during the call. Also you can use a Wait
method for a task, wrapped into a try/catch
clause, and check the task state after that, and deal with it as you need (it has IsFaulted
, IsCompleted
, IsCanceled
布尔属性)。
此外,强烈建议为您的 task-oriented 任务使用一些取消逻辑,以便能够取消不必要的工作。您可以从 this MSDN article 开始。
根据您的其他问题更新:
如果您仍然想使用 ContinueWith
而不是 await
,并且想更改 Job1
、Job2
方法的签名,您应该更改你的代码是这样的:
Delegate.Job1(request)
.ContinueWith(_ => Delegate.Job2(request), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(Result, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted);
原因是 ContinueWith
方法接受 Func<Task, Task>
,因为一般来说,您需要检查任务的状态 and/or 它是结果。
关于不屏蔽来电的问题,你可以试试TaskCompletionSource<TResult>
class,像这样:
public void ICannotBeAsync()
{
var source = new TaskCompletionSource<TResult>();
var task = Task.Run(() => caller.DoSomethingAsync(request, source);
while (!source.IsCompleted && !source.IsFaulted)
{
// yeild the execution to other threads for now, while the result isn't available
Thread.Yeild();
}
}
public async Task DoSomethingAsync(MyRequest request, TaskCompletionSource<TResult> source)
{
request.result1 = await delegateInstance.Job1(request);
request.result2 = await delegateInstance.Job2(request);
Console.Writeline(request.result1 + " " + request.result2);
source.SetResult(result);
}
这里说了很多,所以我只回答最后"Problem"部分:
Customer.GetCustomersAsync(request)
.ContinueWith(_ => { Debug.WriteLine("not executing"); Fault(request); }, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(_ => { Debug.WriteLine("not executing either"); Result(request); }, TaskContinuationOptions.OnlyOnRanToCompletion);
这里的问题(在原始示例中也是如此)是发生以下情况:
- 您继续
GetCustomersAsync
并继续 "only on faulted"。
- 然后你继续那个继续,而不是
GetCustomersAsync
,下一个继续,它只能在完成时运行。
结果,当GetCustomersAsync
失败时,两个continations 只能执行。修复:
var request = Customer.GetCustomersAsync(request);
request.ContinueWith(_ => { Debug.WriteLine("not executing"); Fault(request); }, TaskContinuationOptions.OnlyOnFaulted);
request.ContinueWith(_ => { Debug.WriteLine("not executing either"); Result(request); }, TaskContinuationOptions.OnlyOnRanToCompletion);
请注意,即使您不能更改某些方法的签名并且它绝对必须 return 无效,您仍然可以将其标记为异步:
public async void DoSomethingAsync(MyRequest request)
{
try {
await Customer.GetCustomersAsync(request);
Result(request);
}
catch (Exception ex) {
Fault(request);
}
}
编辑 请参阅我问题末尾的标题 "Problem" 以破解此问题。
来自我们可以链接承诺的 nodejs,在 C# 中,我看到异步任务几乎具有可比性。这是我的尝试。
编辑 - 我无法将超级级别的调用方方法标记为 async
,因为基于 dll 的库正在调用它
来电object
public void DoSomething(MyRequest request)
{
Delegate.Job1(request)
.ContinueWith(Delegate.Job2)
.ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(Result);
}
public void Result(Task<MyRequest> task)
{
MyRequest request = task.Result;
Console.Writeline(request.result1 + " " + request.result2);
}
public void Fault(Task<MyRequest> task)
{
MyRequest request = task.Result;
Console.Writeline(request.result);
}
代表Object
public async Task<MyRequest> Job1(MyRequest request)
{
var data = await remoteService.Service1Async();
request.result1 = data;
return request;
}
public async Task<MyRequest> Job2(Task<MyRequest> task)
{
var data = await remoteService.Service2Async();
request.result2 = data;
return request;
}
问题:
1) 编辑(已修复,链接到我的项目的 dll 丢失了它的链接 dll)Task.Result
(请求)在 Result
方法中为 null,另外 Status = Faulted
2) 故障处理是否正确?我希望只有在委托方法中发生异常时才会调用 Fault,否则应该跳过。
2-b) 另一种选择是在 Result
函数中检查(删除 Fault
函数)如果 Task.status = RanTocompletion
并在那里分支以判断成功或错误
回答后编辑
我有一个问题,如果我不能让我的控制器异步怎么办。
控制器
public void ICannotBeAsync()
{
try
{
var r = await caller.DoSomething(request); // though I can use ContinueWith here, ???
}
catch(Exception e)
{
//exception handling
}
}
来电
public async Task DoSomethingAsync(MyRequest request)
{
request.result1 = await delegateInstance.Job1(request);
request.result2 = await delegateInstance.Job2(request);
Console.Writeline(request.result1 + " " + request.result2);
return result;
}
编辑 2 - 基于 VMAtm 编辑,请查看 OnlyOnFaulted(故障)选项。
Delegate.Job1(request)
.ContinueWith(_ => Delegate.Job2(request), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(() => {request.result = Task.Exception; Fault(request);}, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(Result, TaskContinuationOptions.OnlyOnRanToCompletion);
问题 -
测试一下,下面的实际代码,Result
或 Fault
的 none 被调用,尽管方法 GetCustomersAsync
返回成功。我的理解是一切都在 Fault
处停止,因为它仅在 Fault
上被标记为 运行,执行在那里停止并且未调用 Result
处理程序。
Customer.GetCustomersAsync(request)
.ContinueWith(_ => { Debug.WriteLine("not executing"); Fault(request); }, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(_ => { Debug.WriteLine("not executing either"); Result(request); }, TaskContinuationOptions.OnlyOnRanToCompletion);
编辑 3 基于 Evk 的回答。
Task<Request> task = Customer.GetCustomersAsync(request);
task.ContinueWith(_ => Job2Async(request), TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(_ => Job3Async(request), TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(_ => Result(request), TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t => { request.Result = t.Exception; Fault(request); }, TaskContinuationOptions.OnlyOnFaulted);
此代码存在多个问题:
Delegate.Job1(request)
.ContinueWith(Delegate.Job2)
.ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(Result);
首先,即使 Delegate.Job1
失败,您仍将继续执行 Delegate.Job2
。所以这里需要一个 OnlyOnRanToCompletion
值。与 Result
延续类似,您在所有情况下都在继续,因此出现错误的任务仍会通过链,并且如您所见,处于 Faulted
状态 null
结果。
因此,如果您不能在该级别 await
上使用,您的代码可能是这样的(另外,正如@Evk 所述,您必须将异常处理添加到所有代码中,这真的很难看):
Delegate.Job1(request)
.ContinueWith(Delegate.Job2, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(Result, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted);
但是,您仍然可以选择在您的方法中使用 await
关键字,然后使用 lambda 同步地 运行 它,如果您可以选择:
public async Task DoSomethingAsync(MyRequest request)
{
try
{
request.result1 = await delegateInstance.Job1(request);
request.result2 = await delegateInstance.Job2(request);
Console.Writeline(request.result1 + " " + request.result2);
return result;
}
catch(Exception e)
{
}
}
public void ICannotBeAsync()
{
var task = Task.Run(() => caller.DoSomethingAsync(request);
// calling the .Result property will block current thread
Console.WriteLine(task.Result);
}
异常处理可以在任一级别上完成,因此由您决定在何处引入它。如果在执行过程中出现问题,Result
property will raise an AggregateException
as a wrapper to inner exceptions happened during the call. Also you can use a Wait
method for a task, wrapped into a try/catch
clause, and check the task state after that, and deal with it as you need (it has IsFaulted
, IsCompleted
, IsCanceled
布尔属性)。
此外,强烈建议为您的 task-oriented 任务使用一些取消逻辑,以便能够取消不必要的工作。您可以从 this MSDN article 开始。
根据您的其他问题更新:
如果您仍然想使用 ContinueWith
而不是 await
,并且想更改 Job1
、Job2
方法的签名,您应该更改你的代码是这样的:
Delegate.Job1(request)
.ContinueWith(_ => Delegate.Job2(request), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(Result, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted);
原因是 ContinueWith
方法接受 Func<Task, Task>
,因为一般来说,您需要检查任务的状态 and/or 它是结果。
关于不屏蔽来电的问题,你可以试试TaskCompletionSource<TResult>
class,像这样:
public void ICannotBeAsync()
{
var source = new TaskCompletionSource<TResult>();
var task = Task.Run(() => caller.DoSomethingAsync(request, source);
while (!source.IsCompleted && !source.IsFaulted)
{
// yeild the execution to other threads for now, while the result isn't available
Thread.Yeild();
}
}
public async Task DoSomethingAsync(MyRequest request, TaskCompletionSource<TResult> source)
{
request.result1 = await delegateInstance.Job1(request);
request.result2 = await delegateInstance.Job2(request);
Console.Writeline(request.result1 + " " + request.result2);
source.SetResult(result);
}
这里说了很多,所以我只回答最后"Problem"部分:
Customer.GetCustomersAsync(request)
.ContinueWith(_ => { Debug.WriteLine("not executing"); Fault(request); }, TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith(_ => { Debug.WriteLine("not executing either"); Result(request); }, TaskContinuationOptions.OnlyOnRanToCompletion);
这里的问题(在原始示例中也是如此)是发生以下情况:
- 您继续
GetCustomersAsync
并继续 "only on faulted"。 - 然后你继续那个继续,而不是
GetCustomersAsync
,下一个继续,它只能在完成时运行。
结果,当GetCustomersAsync
失败时,两个continations 只能执行。修复:
var request = Customer.GetCustomersAsync(request);
request.ContinueWith(_ => { Debug.WriteLine("not executing"); Fault(request); }, TaskContinuationOptions.OnlyOnFaulted);
request.ContinueWith(_ => { Debug.WriteLine("not executing either"); Result(request); }, TaskContinuationOptions.OnlyOnRanToCompletion);
请注意,即使您不能更改某些方法的签名并且它绝对必须 return 无效,您仍然可以将其标记为异步:
public async void DoSomethingAsync(MyRequest request)
{
try {
await Customer.GetCustomersAsync(request);
Result(request);
}
catch (Exception ex) {
Fault(request);
}
}