如何更新此代码以正确运行?
很简单:不要使用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),但这个问题不是你遇到的问题的原因。 –