The Top 7 Mistakes Newbies Make with Akka.NET解释了为什么在演员中使用async / await通常是一个坏主意:
[...]我们看到最终用户在单个消息处理程序中开发了大量嵌套的异步/等待操作。大多数用户忽略了这样做的成本:演员不能在每次等待操作之间处理任何其他消息,因为那些等待仍然是原始消息的“一次消息”保证的一部分!
然而在Petabridge Akka .NET Bootcamp的Unit 3 Lesson 4中,这个例子被认为是好的:
// asynchronously download the image and pipe the results to ourself
_httpClient.GetAsync(imageUrl).ContinueWith(httpRequest =>
{
var response = httpRequest.Result;
// successful img download
if (response.StatusCode == HttpStatusCode.OK)
{
var contentStream = response.Content.ReadAsStreamAsync();
try
{
contentStream.Wait(TimeSpan.FromSeconds(1));
return new ImageDownloadResult(image,
response.StatusCode, contentStream.Result);
}
catch //timeout exceptions!
{
return new ImageDownloadResult(image, HttpStatusCode.PartialContent);
}
}
return new ImageDownloadResult(image, response.StatusCode);
},
TaskContinuationOptions.ExecuteSynchronously)
.PipeTo(Self);
我理解这一点的方式,在GetAsync()
和ContinueWith()
完成之前,演员将无法处理任何其他消息,这正是PipeTo()
试图避免的问题。
我在这里错过了什么吗?
答案 0 :(得分:2)
TaskContinuationOptions.ExecuteSynchronously
表示ContinueWith
将与将任务转换为完成的代码(existing solution)同步运行。
示例中的代码将触发任务,设置ContinueWith()
和PipeTo()
,然后返回。演员可以自由地接收新消息,当任务完成时,PipeTo()
将向其发送结果消息。
正如吉吉提到的那样,如果要在演员中等待任务,那么它将被阻止。
认为ContinueWith()
也会返回任务以及PipeTo()
操作的内容可能会有所帮助。
这是TaskContinuationOptions中的PipeTo扩展:
public static Task PipeTo<T>(this Task<T> taskToPipe, ICanTell recipient, IActorRef sender = null, Func<T, object> success = null, Func<Exception, object> failure = null)
{
sender = sender ?? ActorRefs.NoSender;
return taskToPipe.ContinueWith(tresult =>
{
if (tresult.IsCanceled || tresult.IsFaulted)
recipient.Tell(failure != null
? failure(tresult.Exception)
: new Status.Failure(tresult.Exception), sender);
else if (tresult.IsCompleted)
recipient.Tell(success != null
? success(tresult.Result)
: tresult.Result, sender);
}, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
答案 1 :(得分:1)
我认为发生的事情是,由于没有等待GetAsync()
,所以整个事情都是异步发射的,不会阻止执行。
虽然发生了这一切,演员可以自由处理其他消息。