C#多线程TPL常见操作误区与异常处理

C#多线程TPL常见操作误区与异常处理

前言

随着计算机硬件性能的不断提升,多线程编程已经成为了现代程序设计的重要组成部分。而C#作为现代编程语言之一,它自身所提供的多线程处理库TPL(Task Parallel Library)也变得越来越重要。

然而,TPL虽然极为强大且易于使用,但在使用过程中仍存在一些常见的操作误区和异常情况,如果不注意会给系统带来严重的性能问题和安全问题。本文将详细介绍TPL的常见误区和异常处理策略,以确保您在开发过程中能够用TPL编写出稳健高效的多线程代码。

误区

误区1:避免使用同步等待

在编写TPL代码过程中,我们常常会用到等待任务完成的操作。通常,使用同步等待(.Wait())的方式来等待任务完成是最为直观的,但这种方式可能会导致代码的性能急剧下降。原因是同步等待会阻塞当前线程,等待过程中无法处理其他任务,这样很容易导致系统的资源浪费。

解决方法是使用异步等待(await)方式,它会让当前线程在等待任务完成的过程中处理其他任务,从而提高了系统的资源利用率。

下面的示例展示了同步等待和异步等待之间的性能差异:

private static async Task<int> MyLongRunningMethodAsync()
{
    int result = 0;
    await Task.Delay(1000);
    for (int i = 0; i < 10000000; i++)
    {
        result += i;
    }
    return result;
}

private static void TestSyncWait()
{
    Stopwatch sw = Stopwatch.StartNew();
    Task<int> task = MyLongRunningMethodAsync();
    task.Wait();
    Console.WriteLine("Result: {0}, ElapsedMilliseconds: {1}", task.Result, sw.ElapsedMilliseconds);
}

private static async Task TestAsyncWait()
{
    Stopwatch sw = Stopwatch.StartNew();
    int result = await MyLongRunningMethodAsync();
    Console.WriteLine("Result: {0}, ElapsedMilliseconds: {1}", result, sw.ElapsedMilliseconds);
}

TestSyncWait(); // 输出:Result: 49999995000000, ElapsedMilliseconds: 1002
TestAsyncWait().Wait(); // 输出:Result: 49999995000000, ElapsedMilliseconds: 1001

可以看到,异步等待整个方法只花费了1001ms,而同步等待在等待1000ms以后,整个方法花费了1002ms。因此,在TPL编程过程中应尽量避免使用同步等待。

误区2:不要忽略异常处理

在开发过程中,异常处理是必不可少的。然而,在TPL编程过程中,异常处理更为重要。由于TPL支持多线程、异步操作,因此很容易出现异常情况,这些异常如果不加处理,会给整个系统的稳定性带来风险。

因此,在使用TPL进行编程时,应当重视异常处理,及时捕获和处理异常信息,以保证系统的稳定性和安全性。另外,在捕获异常后,需要考虑如何终止任务的执行,避免造成额外的资源浪费。

下面的示例展示了如何处理TPL中的异常:

private static async Task<int> MyLongRunningMethodAsync()
{
    await Task.Delay(1000);
    throw new Exception("Something went wrong...");
}

private static async Task TestExceptionsAsync()
{
    int result = 0;
    try
    {
        result = await MyLongRunningMethodAsync();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    Console.WriteLine("Result: {0}", result);
}

TestExceptionsAsync().Wait(); // 输出:Something went wrong...,Result: 0

在上面的示例中,我们在MyLongRunningMethodAsync方法中抛出了一个异常,然后在TestExceptionsAsync方法中捕获并打印了异常信息。另外,在异常处理后,我们使用了默认值0来代替损坏的结果。

异常处理

在使用TPL编程时,我们应当时刻警惕任务执行过程中的异常情况,及时捕获和处理。除了前面提到的异常处理技巧外,下面是另外一些异常处理的技巧。

抢占式取消

在很多情况下,我们需要实现抢占式取消,即在某个任务完成之前,我们需要终止任务的执行。而TPL提供的CancelationToken机制就能够解决这个问题。下面的示例展示了如何使用CancelationToken机制实现任务取消:

private static async Task<int> MyLongRunningMethodAsync(CancellationToken token)
{
    int result = 0;
    await Task.Delay(1000, token);
    for (int i = 0; i < 10000000; i++)
    {
        token.ThrowIfCancellationRequested();
        result += i;
    }
    return result;
}

private static async Task TestCancellationTokenAsync()
{
    var cts = new CancellationTokenSource(500);
    int result = 0;
    try
    {
        result = await MyLongRunningMethodAsync(cts.Token);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    Console.WriteLine("Result: {0}", result);
}

TestCancellationTokenAsync().Wait(); // 输出:A task was canceled.,Result: 0

在上面的示例中,我们在MyLongRunningMethodAsync方法中使用CancellationTokenSource来限制任务执行时间为500ms,当任务超时时,会自动触发CancelationToken机制,终止任务的执行。

异常链

在TPL编程过程中,可能会出现多个任务例如在执行过程中抛出异常情况,如果只是简单的抛出异常信息,会让调试变得非常困难。因此,可以使用异常链技巧,将不同任务中抛出的异常信息通过Exception.InnerException属性串联起来,从而让程序员更好的进行调试。

下面的示例展示了如何使用异常链技巧:

private static async Task<int> MyLongRunningMethodAsync()
{
    await Task.Delay(1000);
    throw new Exception("Subtask exception");
}

private static async Task<int> MyMainTaskAsync()
{
    try
    {
        int result = await MyLongRunningMethodAsync();
        return result;
    }
    catch (Exception ex)
    {
        throw new Exception("Main task error", ex);
    }
}

private static async Task TestExceptionChainAsync()
{
    int result = 0;
    try
    {
        result = await MyMainTaskAsync();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        Console.WriteLine(ex.InnerException.Message);
    }
    Console.WriteLine("Result: {0}", result);
}


TestExceptionChainAsync().Wait(); // 输出:Main task error,Subtask exception,Result: 0

在上面的示例中,我们在MyLongRunningMethodAsync方法中抛出了一个异常,然后在MyMainTaskAsync中将其封装为一个“Main task error”的异常,最后通过Exception.InnerException属性串联起来。在TestExceptionChainAsync中,我们捕获了Main task error异常,并打印了异常链信息。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#多线程TPL常见操作误区与异常处理 - Python技术站

(0)
上一篇 2023年5月15日
下一篇 2023年5月15日

相关文章

  • C#算法之回文数

    C#算法之回文数 什么是回文数? 回文数指的是正着读和反着读都相同的数字。 例如,121、1331、2332等都是回文数。 判断一个数字是否为回文数的思路 判断一个数字是否为回文数,可以先把这个数字变成字符串,然后判断字符串正着读和反着读是否一致。 还可以采用“双指针”法,从数字的两端向中间靠拢,判断每一位是否一致。 C#代码实现 方法一:将数字转化为字符串…

    C# 2023年6月7日
    00
  • C# Double转化为String时的保留位数及格式方式

    下面是详细的讲解“C# Double转化为String时的保留位数及格式方式”的完整攻略。 格式化字符串 在 C# 中,可以使用 string.Format() 或 $”” 来将 double 类型转化为字符串。这两种方式都可以通过格式化字符串来进行保留位数及格式的设置。 保留固定位数 保留 double 类型变量小数点后的固定位数有两种方式: 第一种,使用…

    C# 2023年6月8日
    00
  • VS2008中使用JavaScript调用WebServices

    VS2008中使用JavaScript调用WebServices的完整攻略 在VS2008中,我们可以使用JavaScript调用WebServices。本文将提供详细的“VS2008中使用JavaScript调用WebServices”的完整攻略,包括如何创建WebServices、如何使用JavaScript调用WebServices以及两个示例。 创建…

    C# 2023年5月15日
    00
  • C# winform打开Excel文档的方法总结(必看篇)

    以下是对“C# winform打开Excel文档的方法总结(必看篇)”的完整攻略: 简介 在C# WinForm开发中,我们经常需要读取并操作Excel文档。本文将介绍几种可行的Excel文档打开方法。 使用OleDb方式打开Excel文档 用Visual Studio创建一个新的WinForm程序项目,引用System.Data.OleDb应用程序集。 在…

    C# 2023年6月1日
    00
  • C# Console类的具体用法

    C# Console类是用于在控制台中进行输入和输出操作的类。它提供了方便易用的方法来进行控制台输入输出,是C#语言中常用的一个类,下面我们来具体讲解其用法。 Console类简介 在C#中,Console类是System命名空间的一个静态类,它提供了在控制台应用程序中读取和写入控制台输入和输出的方法。其中最常用的方法有以下几种: Console.Write…

    C# 2023年5月31日
    00
  • MASA MinimalAPI源码解析:为什么我们只写了一个app.MapGet,却生成了三个接口

    源码解析:为什么我们只写了一个app.MapGet,却生成了三个接口 1.ServiceBase 1.AutoMapRoute 源码如下: AutoMapRoute自动创建map路由,MinimalAPI会根据service中的方法,创建对应的api接口。 比如上文的一个方法: public async Task<WeatherForecast[]&g…

    C# 2023年5月5日
    00
  • asp.net 操作excel的实现代码

    下面我来详细讲解一下“ASP.NET操作Excel的实现代码”的完整攻略,包含以下几个部分: 了解ASP.NET操作Excel的前提条件 使用C#代码操作Excel文件 使用EPPlus操作Excel文件 实例说明:导入Excel数据到ASP.NET网站 实例说明:导出ASP.NET网站数据到Excel文件 了解ASP.NET操作Excel的前提条件 在使用…

    C# 2023年5月31日
    00
  • 在C#中新手易犯的典型缺陷

    在C#中,新手常常会犯下一些典型的编程缺陷。这些缺陷可能影响程序的性能,安全性或可维护性。本攻略将针对这些缺陷进行详细讲解,并提供相应的示例代码以帮助读者更好地理解。 1. 不恰当地使用字符串连接符 在C#中,字符串连接符有两种形式:+和StringBuilder。不恰当地使用字符串连接符可能会导致代码的性能下降。 示例 1:使用字符串连接符进行字符串拼接 …

    C# 2023年5月15日
    00
合作推广
合作推广
分享本页
返回顶部