文章

C#并发

并发

并发可以通过多线程(线程池)、异步编程、响应式编程实现
多线程包括并行(把正在执行的大量的任务分割成小块,分配给多个同时运行的线程)

异步编程

使用两个关键字: async和await async加在方法声明上, 作用是使方法内的await关键字生效, 如果async方法有返回值, 应该返回Task, 如果没有返回值, 应该返回Task, await用来异步等待, 使得方法可异步等待另一个Task执行完成后再继续执行
有些细节需要注意: 1.一定要避免使用Task.Wait 或 Task.Result 方法,因为它们会导致死锁
2.一个async方法第一个同步程序块在调用这个方法的线程中运行, 但其他同步块运行线程需要根据情况决定
每个方法会试图在原始的上下文中恢复运行, 如果在UI线程中调用, 则在此UI线程中执行, 如果在线程池中调用, 则在线程池线程上运行
要避免这种错误行为, 可以在await中调用ConfigAwait方法, 将continueOnCapturedContext设置为false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    
    static async void DoSomething()
    {
        Console.WriteLine(10);
        await DoTask(); //加await则异步等待, 不加则直接往下运行
        Console.WriteLine(20);
    }
    static async Task DoTask() 
    {
        int timer = 10;
        while(timer-->=0)
        {
            Console.WriteLine("Task:" + timer);
            await Task.Delay(TimeSpan.FromSeconds(1));
        } 
    }

并行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
internal partial class Program
{
    //相比 PLINQ,Parallel 类与系统中其他进程配合得更好。而PLINQ 的代码更加简洁
    //由于需要读写共享数据, 使用阻塞锁, 但lock与async不兼容,如果代码块中有使用await则需要使用异步锁
    //数据并行1, 使用Parallel.ForEach
    static int DataParallel_1(List<int> listNumber)
    {
        int sum = 0;
        object readlock = new object();
        //
        Parallel.ForEach(listNumber, val => { lock (readlock) { sum += val; } });
        return sum;
    }

    //数据并行2, 使用PLINQ
    static int DataParallel_2(List<int> listNumber)
    {
        int sum = 0;
        object readlock = new object();
        listNumber.AsParallel().ForAll(val => { lock (readlock) { sum += val; } });

        return sum;
    }

    //任务并行
    static int TaskParallel(List<int> listNumber)
    {
        int result1 = 0, result2 = 0;
        Parallel.Invoke(
            () => GetSum(listNumber, 0, listNumber.Count / 2, out result1),
            () => GetSum(listNumber, listNumber.Count / 2, listNumber.Count, out result2)
            );
        return result1 + result2;
    }
    static void GetSum(List<int> arr, int left, int right, out int result)
    {
        int _result = 0;
        for (int i = left; i < right; i++)
        {
            _result += arr[i];
        }
        result = _result;
    }
    public static void Main4()
    {
        int size = 10000;
        List<int> listNumber = new List<int>(size);
        for (int i = 0; i < size; ++i)
        {
            listNumber.Add(i);
        }
        int result = DataParallel_1(listNumber);
        Console.WriteLine($"DataParallel_1 sum = {result}");

        result = DataParallel_2(listNumber);
        Console.WriteLine($"DataParallel_2 sum = {result}");

        result = TaskParallel(listNumber);
        Console.WriteLine($"TaskParallel sum = {result}");

        Console.ReadLine();
    }
}

本文由作者按照 CC BY 4.0 进行授权