C#基础提升系列——C#任务和并行编程
C#任务和并行编程
我们在处理有些需要等待的操作时,例如,文件读取、数据库或网络访问等,这些都需要一定的时间,我们可以使用多线程,不需要让用户一直等待这些任务的完成,就可以同时执行其他的一些操作。即使是处理密集型的任务,线程也能起到帮助作用, 一个进程的多个线程可以同时运行在不同的CPU上,或多核CPU的不同内核上。
在.NET 4.0之后,提供了线程的一个抽象机制:任务。任务允许建立任务之间的关系(例如,一个任务完成之后可以继续下一个任务),也可以建立一个层级结构,其中包含多个任务。
可以是使用Task
或Parallel
类实现并行活动。
- 数据并行:在不同的任务之间同时处理一些数据。
- 任务并行:同时执行不同的功能。
在创建并行程序时,如果需要管理任务之间的关系,或定义返回任务的方法,就要使用Task
类;如果需要更多的控制并行性,如设置优先级,就需要使用Thread
类;除此之外就可以使用Parallel
类,它提供了非常简单的并行性。
Parallel
类
Parallel
类是对线程的一个很好的抽象。该类提供了数据和任务并行性。如果能用Parallel
类解决的话,优先使用Parallel
类进行处理。
Parallel
类定义了并行的for
和foreach
的静态方法,分别为Parallel.For()
和Parallel.ForEach()
,它们在每次迭代中调用相同的代码,对于C#的for
和foreach
语句而言,循环从一个线程中运行。Parallel
类使用多个任务,因此使用多个线程来完成这个作业。
Parallel
类还有一个静态方法Parallel.Invoke()
,它允许同时调用不同的方法,用于任务并行性,而Parallel.ForEach()
用于数据并行性。
Parallel.For()
Parallel.For()
方法类似于C#的for
循环语句,也是多次执行一个任务(代码块),使用Parallel.For()
方法,可以并行运行迭代。
首先,定义一个方法,用来记录当前任务和线程的标识符。
public static void log(string prefix)
{
Console.WriteLine($"{prefix} \t任务ID: {Task.CurrentId}\t线程ID: {Thread.CurrentThread.ManagedThreadId}");
}
Parallel.For()
方法有许多的重载版本,最简单的定义如下:
public static ParallelLoopResult For(int fromInclusive, int toExclusive, Action<int> body)
该方法的前两个参数定义了循环的开头和结束。第3个参数是一个Action<int>
委托,整数参数是循环的迭代索引。使用示例如下:
public static void ParallelFor()
{
ParallelLoopResult result = Parallel.For(0, 10,
i =>
{
log("开始:" + i);
//这行代码为了创建更多的线程,可以调大时间
Task.Delay(1000).Wait();
log("结束:" + i);
});
Console.WriteLine("循环是否结束:" + result.IsCompleted);
}
Parallel.For()
方法的返回类型是ParallelLoopResult
结构,它提供了循环是否结束的信息。上述示例中的Task.Delay(1000).Wait()
是为了延长遍历的代码块执行时间,以便创建更多的线程。
上述执行的结果如下:
开始:6 任务ID: 2 线程ID: 4
开始:4 任务ID: 1 线程ID: 5
开始:2 任务ID: 4 线程ID: 3
开始:8 任务ID: 5 线程ID: 6
开始:0 任务ID: 3 线程ID: 1
结束:0 任务ID: 3 线程ID: 1
开始:3 任务ID: 3 线程ID: 1
结束:4 任务ID: 1 线程ID: 5
开始:5 任务ID: 12 线程ID: 5
开始:1 任务ID: 11 线程ID: 8
结束:6 任务ID: 2 线程ID: 4
开始:7 任务ID: 13 线程ID: 4
结束:8 任务ID: 5 线程ID: 6
开始:9 任务ID: 14 线程ID: 6
结束:2 任务ID: 4 线程ID: 3
结束:5 任务ID: 12 线程ID: 5
结束:9 任务