目录
并发和并行
在编程领域,并发 和并行是两个密切相关但是又有区别的概念
它们都涉及到同时处理多个任务,但在执行方式,目的和实现上存在差异
一.并发
定义
并发是指一个系统能够同时处理多个任务的能力.在并发执行中,多个任务在逻辑上是"同时"进行的,但在物理上可能并不是同时进行,而是通过在任务之间快速切换实现的
2.
特点
-
**任务交替进行:**在单核处理器上,操作系统通过时间片轮转的方式,使多个任务看起来像是同时进行的
-
**资源共享:**并发任务共享同一资源,需要协调和同步,避免冲突
-
**重点在于结构化程序:**并发性使程序更易于建模和维护,因为它将复杂的流程分解为独立的任务
-
在C#中的体现:
- 异步编程:
- 使用async和await关键字,方法可以在等待I/O操作时释放线程
- 适用于密集型任务,如文件读写,网络通信
- 线程(Thread)和任务(Task):
- 使用System.Threading命名空间下的Thread类
- 使用System.Threading.Tasks命名空间下的Task类,实现更高级的并发模型
- 异步编程:
代码示例
cs
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Task<string> task1 = GetDataAsync("http://example.com/data1");
Task<string> task2 = GetDataAsync("http://example.com/data2");
// 并发执行两个异步任务
string[] results = await Task.WhenAll(task1, task2);
Console.WriteLine(results[0]);
Console.WriteLine(results[1]);
}
static async Task<string> GetDataAsync(string url)
{
using (HttpClient client = new HttpClient())
{
return await client.GetStringAsync(url);
}
}
}
代码解释
- 在上述代码中,GetDataAsync方法是异步的,调用它并不会阻碍主线程
- Task.WhenAll方法并发地等待多个任务完成,尽管在单核CPU上这些任务并不是物理上同时执行的
二.并行
定义
- 并行是指在物理上同时执行多个任务.在并行执行中,多个任务真正地在同一时间被多个处理器或多个处理器核心执行
特点
- **物理上的同时性:**需要硬件支持,如多核 CPU 或多处理器系统
- 提高性能:通过同时执行多个计算密集型任务来缩短总的执行时间
- **任务独立性:**并行任务通常是相互独立的,减少了同步和竞争的需要
在C#中的体现
- 并行LINQ(PLINQ):
- 使用并行化技术加速LINQ查询
- 位于System.LINQ命名空间
- 并行类:
- 位于System.Threading.Tasks下命名空间
- 提供Parallel.For和Parallel.ForEach等方法,轻松实现数据并行
- 任务并行库(Task Parallel Library,TPL):
- 基于任务的并行模型,充分利用多核处理器的性能
代码示例
cs
using System;
using System.Threading.Tasks;
class Program
{
static void Main()
{
// 定义一个大型数组
int[] numbers = new int[100000000];
Parallel.For(0, numbers.Length, i =>
{
numbers[i] = i * i;
});
Console.WriteLine("计算完成。");
}
}
代码解释
Parallel.For方法会自动将循环迭代分配到多个线程,在多个CPU核心上同时运行
- 适用于CPU密集型计算任务,可以显著提高性能
三.并发和并行的区别
|----|-------------------------------|----------------------------|
| | 并发 | 并行 |
| 定义 | 系统处理多个任务的能力,通过任务切换,实现逻辑上的同时执行 | 多个任务在物理上同时执行,需要多核或多处理器硬件支持 |
| 执行 | 任务交替进行,可能不是同时执行 | 任务真正地同时执行 |
| 目的 | 提高资源利用率和系统吞吐量,提高程序的响应性 | 缩短任务的执行时间,提高计算性能 |
| 适用 | I/O 密集型任务,事件驱动程序,GUI 应用 | 计算密集型任务,如科学计算,大数据处理 |
| 实现 | 线程,异步编程,任务调度 | 多线程加上多处理器或多核处理器,并行算法 |
| 挑战 | 需要处理任务同步,共享资源竞争,避免死锁和竞态条件 | 分解任务,负载均衡,减少线程间通信和同步开销 |
四 .如何在C#中选择并发还是并行
1.考虑任务类型
- I/O密集型任务(网络请求,文件读写):
- 使用并发类型,异步编程
- 因为I/O操作速度慢,线程在等待I/O时可以切换执行其他任务,提高效率
- CPU密集型任务(复杂计算,数据处理):
- 使用并行模型,充分利用CPU
- 通过并行算法将任务分解为可同时执行的子任务
2.代码示例
并发异步调用:
cs
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Task<string> task1 = GetDataAsync("http://example.com/data1");
Task<string> task2 = GetDataAsync("http://example.com/data2");
// 并发执行两个异步任务
string[] results = await Task.WhenAll(task1, task2);
Console.WriteLine(results[0]);
Console.WriteLine(results[1]);
}
static async Task<string> GetDataAsync(string url)
{
using (HttpClient client = new HttpClient())
{
return await client.GetStringAsync(url);
}
}
}
并行处理数据:
cs
using System;
using System.Threading.Tasks;
using System.Linq;
class Program
{
static void Main()
{
int[] numbers = Enumerable.Range(0, 1000000).ToArray();
var evenNumbers = numbers.AsParallel()
.Where(n => n % 2 == 0)
.ToArray();
Console.WriteLine($"找到 {evenNumbers.Length} 个偶数。");
}
}
3.注意事项
- 线程安全和同步
- 无论是并发还是并行,都需要处理共享数据的同步问题
- 使用锁,互斥量,信号量和无锁编程技术,避免数据竞争
- 性能权衡
- 并行化需要考虑线程创建和上下文切换的开销
- 任务过小,可能得不偿失;任务过大,可能无法充分利用并行性
- 异常处理
- 并行任务中的异常处理需要格外注意,使用AggregateException捕获并处理
五.总结
- 并发关注在单个处理器上交替执行多个任务,提高资源利用率和响应性
- 并行关注在多个处理器上同时执行多个任务,缩短执行时间,提升计算性能
- 在 C# 中,通过异步编程,线程,任务和并行类库,可以灵活地实现并发和并行,满足不同应用场景的需求