我認爲使用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()。
從哪裏打電話? UI應用程序,asp.net應用程序,控制檯? – Evk
@Evk它來自控制檯應用程序。即使GetIdAsync()在Task.Run內部調用,像這樣:Task.Run(()=> id = GetIdAsync(); ...) – wooohoh
如果它是控制檯應用程序,它實際上是死鎖?帶有異步的死鎖通常發生在具有「UI」或「請求」線程概念的應用程序中,而不是在控制檯應用程序中。 – Evk