2016-11-14 119 views
1

我認爲使用Task.Run調用異步方法不會導致任何死鎖。但它確實:使用Task.Run同步調用異步方法結束了死鎖

async Task<string> GetIdAsync() 
{ 
    return Task.Run(() => IncInt64Async("id", 1L)).Result.ToString(); // Deadlock 
} 

async Task<Int64> IncInt64Async(string key, Int64 inc); 

我知道我應該使用await。所以這一個作品:

async Task<string> GetIdAsync() 
{ 
    return await IncInt64Async("id", 1L); // of course, no deadlock. 
} 

但我只是想明白爲什麼第一次嘗試造成了死鎖。在我對異步的理解中,死鎖只發生在異步方法捕獲同步上下文並嘗試在上下文無法讓它返回上下文時發生。

此外,GetIdAsync()被稱爲Task.Run(內),所以它不同步的上下文反正運行:

Task.Run(() => { 
    some code... 
    id = getIdAsync(); 
    some code... 
} 
); 

所以我瘋了,並嘗試了所有我能想到的變化,但仍然是所有企圖引起死鎖:

Task.Run(() => IncInt64Async("id", 1L).ConfigureAwait(false).GetAwaiter().GetResult()).ConfigureAwait(false).GetAwaiter().GetResult(); 
IncInt64Async("id", 1L).ConfigureAwait(false).GetAwaiter().GetResult(); 
Task.Run(() => IncInt64Async("id", 1L)).Result; 
Task.Run(() => IncInt64Async("id", 1L)).ConfigureAwait(false).GetAwaiter().GetResult(); 
Task.Run(async() => await IncInt64Async("id", inc)).ConfigureAwait(false).GetAwaiter().GetResult(); 
Task.Run(() => { var task = IncInt64Async("id", inc); task.Wait(); return task.Result; }).Result; 

BTW,IncInt64Async實行內部,它使用幾個等待沒有ConfigureAwait(假)。

可能是什麼原因?


IncInt64Async()的執行:

async Task<Int64> IncInt64Async(string key, Int64 value) 
{ 
    Task task = null; 
    byte error = 1; 
    byte[] bytes = null; 
    lock (c.WriteLock) 
    { 
     c.Write(key, value); 

     _readTask = task = _readTask.ContinueWith(async o => 
     { 
      if ((error = await c.ReadByteAsync()) == 0) // **BLOCKS HERE** 
      { 
       bytes = new byte[ 8 ]; 
       await c.ReadAsync(bytes, 0, 8); 
      } 
      NumWaits--; 
     }).Unwrap(); 
    } 

    if (task != null) 
     await task.AnyContext(); 

    if (error != 0) 
     throw new InvalidOperationException(); 

    return BitConverter.ToInt64(bytes, 0); 
} 

'C' 是TcpHandler的一個實例。

它基本上是通過tcp寫的,並從tcp讀取。

我正在使用ContinueWith()來對齊讀取。 我試着將TaskScheduler.Default傳遞給ContinueWith()。

+0

從哪裏打電話? UI應用程序,asp.net應用程序,控制檯? – Evk

+0

@Evk它來自控制檯應用程序。即使GetIdAsync()在Task.Run內部調用,像這樣:Task.Run(()=> id = GetIdAsync(); ...) – wooohoh

+0

如果它是控制檯應用程序,它實際上是死鎖?帶有異步的死鎖通常發生在具有「UI」或「請求」線程概念的應用程序中,而不是在控制檯應用程序中。 – Evk

回答

0

我已經嘗試了所有在LinqPad上的控制檯應用程序中建議的選項,並且沒有任何內容會被阻止,當然我對IncInt64Async做了一個假設,但沒有死鎖,請修改它以顯示被阻止的內容。這純粹是爲了學習,這些肯定不是很好的實現async-await

void Main() 
{ 
    Task.Run(() => GetIdAsync()).Result.Dump(); 
} 

async Task<string> GetIdAsync() 
{ 
    // return Task.Run(() => IncInt64Async("id",1L)).Result; // 1. 
    // return IncInt64Async("id", 1L).ConfigureAwait(false).GetAwaiter().GetResult(); // 2. 
    // return Task.Run(() => IncInt64Async("id",1L).ConfigureAwait(false).GetAwaiter().GetResult()).ConfigureAwait(false).GetAwaiter().GetResult(); // 3. 
    // return Task.Run(() => IncInt64Async("id",1L)).ConfigureAwait(false).GetAwaiter().GetResult(); // 4. 
    // return Task.Run(async() => await IncInt64Async("id", 1L)).ConfigureAwait(false).GetAwaiter().GetResult(); // 5. 
    return Task.Run(() => { var task = IncInt64Async("id", 1L); task.Wait(); return task; }).Result; // 6. 
} 

public async Task<string> IncInt64Async(string id, long l) 
{ 
    await Task.Delay(1000); 

    return "Test"; 
} 
+0

感謝您的努力。我也嘗試了一個最小的例子,但它運行正常,正如你所說的。似乎IncInt64Async()的實現細節導致了死鎖。 – wooohoh