如何更新此代碼以正確運行?
很簡單:不要使用ContinueWith
。使用await
代替:
public void ThreadTest()
{
try
{
var currentTasks = new List<Task>();
SemaphoreSlim maxThread = new SemaphoreSlim(2);
for (int i = 1; i < 5; ++i)
{
maxThread.Wait();
var testTask = TestAsync(maxThread);
currentTasks.Add(testTask);
}
Task.WaitAll(currentTasks.ToArray());
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
private async Task TestAsync(SemaphoreSlim maxThread)
{
try
{
await FaultyAsync();
}
finally
{
maxThread.Release();
}
}
private async Task FaultyAsync()
{
throw new Exception("Never reach the awaiter");
await Task.Run(() => Thread.Sleep(3000));
}
我也做了一些其他的變化:增加了一個Async
後綴跟隨async naming convention,由於StartNew
is dangerous與Run
取代StartNew
(我形容我的博客)。
該代碼仍然不完全正確。你面臨的問題是:你想要異步並行還是並行併發?這一切都歸結於FaultyAsync
的Task.Run(() => Thread.Sleep(3000))
行。
如果這是一個真正異步的佔位符(例如,,I/O)操作,則ThreadTest
應當異步和使用Task.WhenAll
代替WaitAll
,因爲這樣的:
public async Task TestAsync()
{
try
{
var currentTasks = new List<Task>();
SemaphoreSlim throttle = new SemaphoreSlim(2); // Not "maxThread" since we're not dealing with threads anymore
for (int i = 1; i < 5; ++i)
{
var testTask = TestAsync(throttle);
currentTasks.Add(testTask);
}
await Task.WhenAll(currentTasks);
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
private async Task TestAsync(SemaphoreSlim throttle)
{
await throttle.WaitAsync();
try
{
await FaultyAsync();
}
finally
{
maxThread.Release();
}
}
private async Task FaultyAsync()
{
throw new Exception("Never reach the awaiter");
await Task.Delay(3000); // Naturally asynchronous operation
}
在另一方面,如果Task.Run(() => Thread.Sleep(3000))
爲佔位符真正同步(例如,CPU)操作,那麼你應該使用更高級別的並行抽象的,而不是通過手工創建自己的任務:
public void ThreadTest()
{
try
{
var options = new ParallelOptions { MaxDegreeOfParallelism = 2 };
Parallel.For(1, 5, options, i => Faulty());
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
private void Faulty()
{
throw new Exception("Never reach the work");
Thread.Sleep(3000); // Naturally synchronous operation
}
[你不應該使用'StartNew'沒有傳遞任務schedueller(HTTP://blog.stephencleary。 com/2013/08/startnew-is-dangerous.html),使用'Task.Run('而不是。也可以用整個行代替mor e高效'等待Task.Delay(3000)' –
@ScottChamberlain,謝謝! - 我真的沒有太注意這一點,因爲我的觀點是,我從來沒有達到這一線 - 我拋出一個例外,旨在要求如何處理拋出的東西.... –
我看到了,它仍然是一個非常糟糕的習慣,你應該嘗試打破它。 [ContinueWith有同樣的問題](http://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html),但這個問題不是你遇到的問題的原因。 –