我是一名线程新手,我正在尝试使用SemaphoreSlim
来允许我同时运行一定数量的长任务。
我的挑战是,鉴于我编写它的方式,任何例外都没有被正确捕获。
以下是我当前代码的一个非常简化的示例:
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 = Faulty().ContinueWith(tsk => maxThread.Release());
currentTasks.Add(testTask);
}
Task.WaitAll(currentTasks.ToArray());
Debug.WriteLine("End - We shouldn't have gotten here");
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
private async Task Faulty()
{
throw new Exception("Never reach the awaiter");
await Task.Factory.StartNew(() => Thread.Sleep(3000));
}
而且,不幸的是,在那里ContinueWith
,我进入了“结束 - 我们不应该在这里”的消息,而不是我想要的错误消息。
如何更新此代码才能正常运行?我再次道歉,如果这是完全错误的,这是一个新手尝试将我在网上找到的东西放在一起的东西 - 任何和所有正确做到这一点的建议真的很感激!!!
答案 0 :(得分:3)
如何更新此代码才能正常运行?
非常简单:不要使用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
替换为Run
,因为StartNew
is dangerous(如我在我的博客上描述。)
代码仍然没有完全正确。你的问题是:你想要异步并发还是并行并发?这一切都归结为Task.Run(() => Thread.Sleep(3000))
中的FaultyAsync
行。
如果这是真正异步(例如,I / O)操作的占位符,则ThreadTest
应该异步并使用Task.WhenAll
而不是WaitAll
,如下所示:< / p>
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
}
答案 1 :(得分:0)
这与异步任务中如何处理异常有关。
根据Microsoft的网站(https://msdn.microsoft.com/en-us/magazine/jj991977.aspx):
从异步任务或异步任务中抛出异常时 方法,捕获异常并将其放在Task对象
上
这意味着当您在异步方法中抛出异常时,它应该获取该异常并将其放在任务对象本身上。它甚至继续在网站上给出一个例子,如果返回了一个Task对象,那么异常永远不会在主线程中抛出,因为它被放在Task对象上。
听起来您需要检查Task对象以查看它是否有效或包含异常。
答案 2 :(得分:0)
您可以将ThreadTest函数标记为异步并使用:await Faulty();在try-catch块中,你可以捕获异常。