C#-使用ContinueWith进行错误传播 [英] C# - Error propagation with ContinueWith

查看:162
本文介绍了C#-使用ContinueWith进行错误传播的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是一名线程新手,正尝试使用 SemaphoreSlim 允许我同时运行一定数量的长任务。

I'm a threading newbie and am trying to use SemaphoreSlim to allow me to run a set number of long tasks simultaneously.

我的挑战是,按照我写的方式,任何异常都无法正确捕获。

My challenge is that, given the way I've written it, any exceptions are not being captured correctly.

这是我的一个非常简化的示例当前代码:

Here's a very simplified example of my current code:

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 进入结束-我们不应该到这里消息,而不是我想要得到的错误消息。

And, unfortunately, with the ContinueWith in there, I get to the "End - We shouldn't have gotten here" message rather than the error message I'd have wanted to get to.

如何更新此代码以使其正常运行?再次致歉,如果这是完全错误的,这是新手尝试将我在网上找到的内容整理到一起的方法-真的很感谢任何正确建议的建议!

How could I update this code to run correctly? Again, I apologize if this is completely wrong, this is a newbie's attempts at putting stuff together from stuff I've found online - Any and all suggestions to do this correctly are really appreciated!!!

推荐答案


如何更新此代码以使其正常运行?

How could I update this code to run correctly?

非常简单:不要使用 ContinueWith 。使用 await 代替:

Pretty simple: don't use ContinueWith. Use await instead:

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));
}

我还做了其他一些更改:添加了异步后缀以遵循异步命名约定,并将 StartNew 替换为 Run ,因为 StartNew 很危险(正如我在我的描述中所述博客)。

I also made a couple of other changes: added an Async postfix to follow the async naming convention, and replaced StartNew with Run since StartNew is dangerous (as I describe on my blog).

该代码最终还是不太正确。您的问题是:您要异步并发还是并行并发?一切都归结于 FaultyAsync Task.Run(()=> Thread.Sleep(3000))行c>。

The code still doesn't end up quite right. The question for you is: do you want asynchronous concurrency or parallel concurrency? And that all comes down to the Task.Run(() => Thread.Sleep(3000)) line in FaultyAsync.

如果这是真正异步(例如I / O)操作的占位符,则 ThreadTest 应该设为异步,并使用 Task.WhenAll 而不是 WaitAll ,例如:

If that's a placeholder for a truly asynchronous (e.g., I/O) operation, then the ThreadTest should be made asynchronous and use Task.WhenAll instead of WaitAll, as such:

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)操作的占位符,那么您应该使用更高级别的并行抽象而不是创建手动完成自己的任务:

On the other hand, if Task.Run(() => Thread.Sleep(3000)) is a placeholder for a truly synchronous (e.g., CPU) operation, then you should use higher-level parallel abstractions instead of creating your own tasks by hand:

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
}

这篇关于C#-使用ContinueWith进行错误传播的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆