c# - 在 akka.net Actor 中安全地使用事件处理程序

标签 c# .net events akka.net

我正在尝试使用 Akka.net 构建一个文件下载 actor。它应该在下载完成时发送消息,但也报告下载进度。

在 .NET 中,有一些类支持使用多个事件的异步操作。例如 WebClient.DownloadFileAsync 有两个事件:DownloadProgressChangedDownloadFileCompleted

最好使用基于任务的异步版本并使用 .PipeTo 扩展方法。但是,我看不出这将如何与公开两个事件的异步方法一起工作。与 WebClient.DownloadFileAsync 的情况一样。即使使用 WebClient.DownloadFileTaskAsync,您仍然需要使用事件处理程序处理 DownloadProgressChanged

我发现使用它的唯一方法是在创建 actor 时连接两个事件处理程序。然后在处理程序中,我向 Self 和 Sender 发送消息。为此,我必须从事件处理程序中引用参与者的一些私有(private)字段。这对我来说是错误的,但我看不到其他出路。

有没有更安全的方法在一个 Actor 中使用多个事件处理程序?

目前,我的解决方案如下所示(_client 是在 actor 的构造函数中创建的 WebClient 实例):

    public void HandleStartDownload(StartDownload message)
    {
        _self = Self;
        _downloadRequestor = Sender;

        _uri = message.Uri;
        _guid = message.Guid;
        _tempPath = Path.GetTempFileName();

        _client.DownloadFileAsync(_uri, _tempPath);
    }

    private void Client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        var completedMessage = new DownloadCompletedInternal(_guid, _tempPath);
        _downloadRequestor.Tell(completedMessage);
        _self.Tell(completedMessage);
    }

    private void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        var progressedMessage = new DownloadProgressed(_guid, e.ProgressPercentage);
        _downloadRequestor.Tell(progressedMessage);
        _self.Tell(progressedMessage);
    } 

所以当下载开始时,一些字段被设置。此外,我确保我 Become 一个状态,在该状态下将进一步隐藏 StartDownload 消息,直到 Self 收到 DownloadCompleted 消息:

    public void Ready()
    {
        Receive<StartDownload>(message => {
            HandleStartDownload(message);
            Become(Downloading);
        });
    }

    public void Downloading()
    {
        Receive<StartDownload>(message => {
            Stash.Stash();
        });
        Receive<DownloadCompleted>(message => {
            Become(Ready);
            Stash.UnstashAll();
        });
    }

作为引用,这是整个 Actor,但我认为重要的内容直接在这篇文章中:https://gist.github.com/AaronLenoir/4ce5480ecea580d5d283c5d08e8e71b5

最佳答案

I must refer to some private fields of the actor from inside the event handlers. This feels wrong to me, but I cannot see another way out.

Is there a safer way to use multiple event handlers in an Actor?

具有内部状态的 actor 以及作为该状态一部分的成员引发在 actor 内处理的事件在本质上没有错。如果采用 OO 方法,这将是错误的。

唯一真正关心的是内部状态是否在多个文件下载请求之间混合,但我认为您当前的代码是合理的。

一个可能更可口的方法可能是将 FileDownloadActor 视为一个单独使用的 actor,启动它,下载文件,将结果告诉发送者,然后杀死 actor。启动 actor 是一种廉价操作,这完全避免了在多个下载请求之间共享内部状态的可能性。

当然,除非您特别需要排队下载以像当前代码那样按顺序运行 - 但队列可以由另一个参与者完全管理,并且仍然将下载参与者视为临时的。

关于c# - 在 akka.net Actor 中安全地使用事件处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37015063/

相关文章:

c# - 从列表中删除相似的矩形

javascript - 对象 #<HTMLInputElement> 没有方法 'initialize'

jQuery:如何组合两个事件: $(document).ready + $ ("#superid a").hover?

c# - 读取两个 JSON 文件并将第一个文件中不存在的第二个文件中存在的所有项目提取到一个新对象

.net - 如果 .NET 程序遇到无效数据,应该抛出什么异常?

c# - 不同开发人员之间的 OOP 方法之间的混淆

javascript - .Net c# webbrowser 填充不带 id 或类名的输入

php - 完整的日历事件数据

c# - 从字符串位转换为字节或十六进制

c# - 添加新的 XML 元素并仍然读取旧版本的 XML 文档