[英]Using event handlers inside an akka.net Actor safely
我正在嘗試使用 Akka.net 構建文件下載角色。 它應該在下載完成時發送消息,但也報告下載進度。
在 .NET 中有一些類支持使用多個事件的異步操作。 例如WebClient.DownloadFileAsync
有兩個事件: DownloadProgressChanged
和DownloadFileCompleted
。
最好是使用基於任務的異步版本並使用.PipeTo
擴展方法。 但是,我看不出這將如何與公開兩個事件的異步方法一起使用。 與WebClient.DownloadFileAsync
的情況一樣。 即使使用WebClient.DownloadFileTaskAsync
您仍然需要使用事件處理程序處理DownloadProgressChanged
。
我發現使用它的唯一方法是在創建我的演員時連接兩個事件處理程序。 然后在處理程序中,我向 Self 和 Sender 發送消息。 為此,我必須從事件處理程序內部引用參與者的一些私有字段。 這對我來說是錯誤的,但我找不到其他出路。
是否有更安全的方法在 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
我必須從事件處理程序內部引用參與者的一些私有字段。 這對我來說是錯誤的,但我找不到其他出路。
是否有更安全的方法在 Actor 中使用多個事件處理程序?
具有內部狀態的actor 以及作為該狀態的一部分的成員引發在actor 內處理的事件,這本身並沒有什么問題。 如果采用面向對象的方法,沒有比這更錯誤的了。
唯一真正關心的是內部狀態是否在多個文件下載請求之間混合,但我認為您當前的代碼是合理的。
一種可能更可口的方法可能是將FileDownloadActor
視為一次性使用的 actor,啟動它,下載文件,將結果告訴發送者,然后殺死 actor。 啟動 actor 是一種廉價的操作,這完全避免了在多個下載請求之間共享內部狀態的可能性。
當然,除非您特別需要像當前代碼一樣將下載排隊以按順序運行 - 但隊列可以完全由另一個演員管理,並且仍然將下載演員視為臨時演員。
我不知道您是否是這種情況,但我看到人們將 Actor 視為簡單的對象時將它們視為微服務。 記住 Actor 有內部狀態。
現在考慮可擴展性,您無法將消息擴展到分布式 Actor 系統中的一個 Actor。 您發送給一個 Actor 的消息將在執行該 Actor 的節點中執行。
如果您想並行執行下載操作(例如),您可以按照帕特里克所說的去做,並為每個下載操作創建一個 Actor,並且該 Actor 可以在任何可用節點中執行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.