2017-08-11 63 views
0
public class EventService 
{ 
    public async Task InsertEventItemAsync(InsertEventItem request) 
    { 
     await SomeMethodThatUsesRestSharpToMakeAsynchronousWebApiCall(request); 
    } 

    public async Task<int> GetNumberOfEvents() 
    { 
     int result = await SomeOtherMethodThatUsesRestSharpToMakeAsynchronousWebApiCall(); 
     return result; 
    } 
} 

public interface IFoo 
{ 
    void Bar(); 
    int Baz(); 
} 

public class Foo : IFoo 
{ 
    EventService _eventService; 

    public void Bar() 
    { 
     Task.Run(async() => await _eventService.InsertEventItemAsync()); 
    } 

    public int Baz() 
    { 
     int result = Task.Run(async() => await this._eventService.GetNumberOfEvents()).Result; 
     return result; 
    } 
} 

對Foo.Bar()中的Task.Run的調用看起來不正確。 這是異步代碼的方法,在最近開始使用的項目的代碼庫中隨處可見。 我的假設是,異步lambda是爲了編譯代碼而編寫的。 我懷疑這是一個好方法。帶有異步lambda的Task.Run

我沒有太多的異步/等待經驗,但我認爲這將啓動一個新的ThreadPool線程並阻塞當前線程,直到任務完成ThreadPool線程上的runninng,這將導致比如果整個批次是同步的。

是代碼「錯誤」?

在我看來,可能會有一種厭惡(或可能是一個很好的理由)一直走異步。我是否應該嘗試使其完全異步或完全同步,而不是嘗試混合它們?

謝謝。

+2

'Foo.Bar'將啓動一個在任務池上執行的新任務。沒有新的線程被創建。當任務異步執行時(「fire and forget」),'Foo.Bar'立即返回。代碼是否錯誤?這取決於你想達到什麼。您可以使用「普通」委託來替換異步委託,以便在生成的IL中進行輕微優化。 –

+0

我已更新問題以顯示如何調用返回任務的方法。如果Task.Run是火,忘了,這是否意味着調用Task.Run(...)。結果阻止Foo。Baz立即返回,而是強迫它等到任務完成?這會增加死鎖的可能性嗎? – dlarkin77

+2

獲取任務的'Result'屬性將阻塞,直到任務完成,因此它不再是「火災和遺忘」。代碼中出現死鎖的可能性應爲零。如果不是,你需要解決死鎖的根本原因。但是,當您的代碼涉及到不可重入代碼的回調時,您可以通過在新任務/線程上安排這些回調來避免死鎖。你的問題似乎是關於異步和等待的一般理解,我建議你研究這個主題和/或寫一些簡單的代碼,以獲得更好的整體理解。 –

回答

0

對Foo.Bar()中的Task.Run的調用看起來不正確。

是的,這是非常糟糕的。

Task.Run在線程池線程上執行其委託。由於它是一個異步委託,它不會在任何時候「等待」時「耗盡」線程池線程 - 因此您不必擔心這一點。

你需要擔心的是從Task.Run返回的任務會發生什麼。特別是,沒有做任何事情。這是有問題的,因爲如果委託引發異常,則該異常將放置在從Task.Run返回的任務上,該任務被完全忽略。所以你有沉默的失敗。這和寫作catch { }到處都是一樣糟糕。

Foo.Baz正在使用thread pool hack(遺憾地使用Result,這使得錯誤處理更加尷尬)。這種黑客攻擊是一種編寫不可能造成死鎖的異步同步代碼的方法。

在我看來,可能會有一種厭惡(或可能是一個很好的理由)一路走下去。我是否應該嘗試使其完全異步或完全同步,而不是嘗試混合它們?

是的。理想的情況下,代碼應該是async all the way(或者一路同步)。在一些情況下這是不可行的,但絕大多數代碼肯定是其中的一種。像傳統代碼更新爲異步的轉換時間可以接受這樣的黑客,但是它們不應該長期存在於沒有充分理由的應用程序中。