C#并发编程

并行处理几种方式

并行处理是一种让程序同时执行多个操作的机制。在C#中,可以使用Parallel类、Task.WhenAll、PLINQ(Parallel LINQ)来实现并行处理


1.Task.WhenAll并发任务

Task.WhenAll 是 C# 中的一个方法,用于等待多个任务(Task 对象)全部完成。


在 C# 中,Task 对象是用于表示异步操作的类。在某些情况下,我们可能需要并行运行多个异步操作,并在所有操作完成后进行处理。这时,可以使用 Task.WhenAll 方法来等待所有任务完成。


Task.WhenAll 方法接受一个 Task 数组,然后返回一个新的 Task 对象,该对象将在所有输入任务都完成时完成。在此期间,控制流会等待,但不会阻塞当前线程。


下面是一个示例,演示如何使用 Task.WhenAll 等待多个任务完成:

using System;
using System.Threading.Tasks;
class Program
{
    static async Task Main()
    {
        // 创建多个任务
        Task<int> task1 = Task.Run(() => 1 + 2);
        Task<int> task2 = Task.Run(() => 3 + 4);
        Task<int> task3 = Task.Run(() => 5 + 6);
        // 等待所有任务完成
        int[] results = await Task.WhenAll(task1, task2, task3);
        // 处理结果
        foreach (int result in results)
        {
            Console.WriteLine(result);
        }
    }
}

在此示例中,创建了三个异步任务,并使用 Task.WhenAll 等待它们全部完成。一旦所有任务都完成,它们的结果将存储在一个整数数组中,并进行处理。

请注意,Task.WhenAll 方法只有在所有任务都成功完成时才会返回成功。如果任何一个任务失败,将会抛出一个异常。


2.Parallel并发循环

Parallel 是 C# 中的一个类,用于支持并行计算。Parallel 类提供了一组方法,可以在多个线程中同时执行某个操作,并且能够自动分配工作负载,以充分利用现代多核 CPU 的优势。


Parallel 类包含多个方法,其中最常用的是 Parallel.For 和 Parallel.ForEach。这两个方法都可以用于并行地遍历一个集合,并在每个元素上执行相同的操作。


以下是一个示例,演示如何使用 Parallel.For 方法并行地计算数组中每个元素的平方:

using System;
using System.Threading.Tasks;
class Program
{
    static void Main()
    {
        int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        
        // 并行计算每个元素的平方
        Parallel.For(0, array.Length, i =>
        {
            array[i] = array[i] * array[i];
        });
        
        // 输出结果
        foreach (int value in array)
        {
            Console.WriteLine(value);
        }
    }
}

在此示例中,使用 Parallel.For 方法并行地计算数组中每个元素的平方。该方法接受三个参数:起始索引、结束索引(不包括)和一个委托,该委托会在每个索引处执行相同的操作。Parallel.For 方法会自动分配工作负载,并使用多个线程并行地执行操作。


在此示例中,每个线程都会计算数组中一部分元素的平方。最后,程序输出结果,以验证计算的正确性。


请注意,由于多个线程同时修改同一个数组,因此在此示例中存在数据竞争(race condition)的风险。在实际编程中,必须采取适当的同步措施,以避免此类问题。


3.PLINQ(Parallel LINQ)并发查询

C# 中的 PLINQ 可以帮助开发人员通过并行处理数据集合来提高查询的性能。以下是 PLINQ 的用法:

引用命名空间:using System.Linq;

调用 AsParallel() 方法将数据集合转换为并行集合。

例如:

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var result = numbers.AsParallel()
                    .Where(x => x % 2 == 0)
                    .ToArray();

上面的代码首先将 numbers 数组转换为并行集合,然后使用 Where() 方法过滤偶数,最后使用 ToArray() 方法将结果转换为数组。


可以使用 WithDegreeOfParallelism() 方法设置并行度,即同时处理元素的数量。

例如:

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var result = numbers.AsParallel()
                    .WithDegreeOfParallelism(4)
                    .Where(x => x % 2 == 0)
                    .ToArray();

上面的代码将并行度设置为 4,即同时处理 4 个元素。


还可以使用 ForAll() 方法对每个元素进行操作,这个方法会并行处理所有元素。

例如:

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
numbers.AsParallel()
       .ForAll(x => Console.WriteLine(x * x));

上面的代码并行地对每个元素求平方,并输出结果。


需要注意的是,PLINQ 并不总是比 LINQ 更快,如果数据集合较小或者查询操作较简单,使用 PLINQ 反而会降低性能。因此,应该根据具体情况来决定是否使用 PLINQ。


4. ORM中使用并行编程

一般需要new 出一个新实例

     Parallel.For(0, array.Length, i =>
     {
           //SqlSugar ORM可以这么写
           db.CopyNew().Queryable<Order>().ToListAsync();
           
           //EF Core
           //需要new 新对象
      });


果糖网