Cool_Breeze 发表于 2021-5-4 13:51

C# 多线程与异步的使用方法笔记

本帖最后由 Cool_Breeze 于 2021-5-16 10:32 编辑

认真阅读这句话:请务必 将任务理解为工作的异步抽象,而非 在线程之上的抽象。 默认情况下,任务在当前线程上执行,且在适当时会将工作委托给操作系统。 可选择性地通过 Task.Run API显式请求任务在独立线程上运行。
# 线程与异步的使用方法

## 使用场景

1. **方法** **AAA, BBB, CCC**

2. 在主线程不阻塞的情况下运行不同的三个方法

3. 方法**CCC**需要在方法**AAA**完成后执行

## 使用线程完成

因为方法 **CCC** 要等待方法 **AAA** 完成,所以需要一个线程同步事件。

```c#
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static EventWaitHandle CCClcok = new EventWaitHandle(false, EventResetMode.ManualReset);
    static void Main()
    {
      Thread tha = new Thread(AAA);
      Thread thb = new Thread(BBB);
      Thread thc = new Thread(CCC);
      tha.Start();
      thb.Start();
      thc.Start();
      Console.WriteLine("等待所有任务完成!");
    }
   
    static void AAA()
    {
      int sec = 10;
      while (sec >= 1)
      {
            Console.WriteLine("方法 AAA: {0} 秒", sec);
            sec--;
            Thread.Sleep(1000);
      }
      Console.WriteLine("方法 AAA 执行完成!");
      CCClcok.Set();
    }
   
    static void BBB()
    {
      int sec = 5;
      while (sec >= 1)
      {
            Console.WriteLine("方法 BBB: {0} 秒", sec);
            sec--;
            Thread.Sleep(1000);
      }
    }
   
    static void CCC()
    {
      CCClcok.WaitOne();
      int sec = 3;
      while (sec >= 1)
      {
            Console.WriteLine("方法 CCC: {0} 秒", sec);
            sec--;
            Thread.Sleep(1000);
      }
    }
}
```



### 线程同步事件

**false** 表示事件为非终止状态,**EventResetMode.ManualReset** 表示手动重置此事件((https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.eventwaithandle?view=netframework-4.8))

```c#
static EventWaitHandle CCClcok = new EventWaitHandle(false, EventResetMode.ManualReset);
```

### 方法 AAA

**CCClcok.Set()** 将事件状态设为终止。被阻塞线程继续 ((https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.eventwaithandle.set?view=netframework-4.8#System_Threading_EventWaitHandle_Set))

```c#
static void AAA()
{
    int sec = 10;
    while (sec >= 1)
    {
      Console.WriteLine("方法 AAA: {0} 秒", sec);
      sec--;
      Thread.Sleep(1000);
    }
    Console.WriteLine("方法 AAA 执行完成!");
    CCClcok.Set();
}
```

### 方法 BBB

无要求

```c#
static void BBB()
{
    int sec = 5;
    while (sec >= 1)
    {
      Console.WriteLine("方法 BBB: {0} 秒", sec);
      sec--;
      Thread.Sleep(1000);
    }
}
```

### 方法 CCC

**CCClcok.WaitOne()** 阻止当前线程,直到当前 (https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.waithandle?view=netframework-4.8) 收到信号。(继承自 (https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.waithandle?view=netframework-4.8))[^ 1 ]

[^ 1 ]: 如果方法 AAA 没有执行 CCClcok.Set() 这里会出现死锁

```c#
static void CCC()
{
    CCClcok.WaitOne();
    int sec = 3;
    while (sec >= 1)
    {
      Console.WriteLine("方法 CCC: {0} 秒", sec);
      sec--;
      Thread.Sleep(1000);
    }
}
```

### Main函数

线程默认为前台线程((https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.thread.isbackground?view=netframework-4.8)),所有主线程会等待所有子线程结束 ((https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.thread?view=netframework-4.8))

```c#
static void Main()
{
    Thread tha = new Thread(AAA);
    Thread thb = new Thread(BBB);
    Thread thc = new Thread(CCC);
    tha.Start();
    thb.Start();
    thc.Start();
    Console.WriteLine("等待所有任务完成!");
}
```

### 执行结果

```powershell
Microsoft (R) Visual C# Compiler version 4.8.3761.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reser

This compiler is provided as part of the Microsoft (R

方法 AAA: 10 秒
方法 BBB: 5 秒
等待所有任务完成!
方法 BBB: 4 秒
方法 AAA: 9 秒
方法 BBB: 3 秒
方法 AAA: 8 秒
方法 AAA: 7 秒
方法 BBB: 2 秒
方法 BBB: 1 秒
方法 AAA: 6 秒
方法 AAA: 5 秒
方法 AAA: 4 秒
方法 AAA: 3 秒
方法 AAA: 2 秒
方法 AAA: 1 秒
方法 AAA 执行完成!
方法 CCC: 3 秒
方法 CCC: 2 秒
方法 CCC: 1 秒
请按任意键继续. . .
```

## 使用异步完成

- 注意事项((https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/async),(https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/await))

    1. **async** 方法内部没有 **await** 关键字将以同步方法运行
    2. **await** 必须出现在 **async** 方法内部
    3. **await** 接一个异步对象(必须是可等待的)

- 不能让主线程直接退出,无法查看后面运行结果!


```c#
Thread.Sleep(14000);
```

```c#
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
      BBB();
      CCC();
      Console.WriteLine("等待所有任务完成!");
      Thread.Sleep(14000);
    }
   
    static async Task AAA()
    {
      int sec = 10;
      while (sec >= 1)
      {
            Console.WriteLine("方法 AAA: {0} 秒", sec);
            sec--;
            await Task.Delay(1000);
      }
      Console.WriteLine("方法 AAA 执行完成!");
    }
   
    static async void BBB()
    {
      int sec = 5;
      while (sec >= 1)
      {
            Console.WriteLine("方法 BBB: {0} 秒", sec);
            sec--;
            await Task.Delay(1000);
      }
    }
   
    static async void CCC()
    {
      await AAA();
      int sec = 3;
      while (sec >= 1)
      {
            Console.WriteLine("方法 CCC: {0} 秒", sec);
            sec--;
            await Task.Delay(1000);
      }
    }
}
```

### 方法 AAA

返回一个 **Task** 对象

```c#
static async Task AAA()
{
    int sec = 10;
    while (sec >= 1)
    {
      Console.WriteLine("方法 AAA: {0} 秒", sec);
      sec--;
      await Task.Delay(1000);
    }
    Console.WriteLine("方法 AAA 执行完成!");
}
```

### 方法 BBB

无需等待,可以不用返回 **Task** 对象

```c#
static async void BBB()
{
    int sec = 5;
    while (sec >= 1)
    {
      Console.WriteLine("方法 BBB: {0} 秒", sec);
      sec--;
      await Task.Delay(1000);
    }
}
```

### 方法 CCC

无需等待,可以不用返回 **Task** 对象

使用 **await** 等待方法 **AAA** 执行完毕

```c#
static async void CCC()
{
    await AAA();
    int sec = 3;
    while (sec >= 1)
    {
      Console.WriteLine("方法 CCC: {0} 秒", sec);
      sec--;
      await Task.Delay(1000);
    }
}
```

### 执行结果

```powershell
Microsoft (R) Visual C# Compiler version 4.8.3761
for C# 5
Copyright (C) Microsoft Corporation. All rights r

This compiler is provided as part of the Microsof

方法 BBB: 5 秒
方法 AAA: 10 秒
等待所有任务完成!
方法 BBB: 4 秒
方法 AAA: 9 秒
方法 BBB: 3 秒
方法 AAA: 8 秒
方法 BBB: 2 秒
方法 AAA: 7 秒
方法 BBB: 1 秒
方法 AAA: 6 秒
方法 AAA: 5 秒
方法 AAA: 4 秒
方法 AAA: 3 秒
方法 AAA: 2 秒
方法 AAA: 1 秒
方法 AAA 执行完成!
方法 CCC: 3 秒
方法 CCC: 2 秒
方法 CCC: 1 秒
请按任意键继续. . .
```

Cool_Breeze 发表于 2021-5-16 09:54

本帖最后由 Cool_Breeze 于 2021-5-16 10:21 编辑

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static Task task;
    static void Main()
    {
      Task.WaitAll(ShowTimeMain(),MonitorShowTimeStatus());
    }
   
    static async Task ShowTimeMain()
    {
      task = ShowTime();
      await task;
    }
   
    static async Task ShowTime()
    {
      await Task.Factory.StartNew
      (
            () =>
            {
                int n = 10;
                while (n >= 0)
                {
                  Console.WriteLine(DateTime.Now);
                  Thread.Sleep(1000);
                  n--;
                }
            }
      );
    }
   
    static async Task MonitorShowTimeStatus()
    {
      if (task == null)
      {
            await Task.Delay(500);
      }
      await Task.Factory.StartNew
      (
            () =>
            {
                while (true)
                {
                  Console.WriteLine(task.Status);
                  if (task.Status == TaskStatus.RanToCompletion)
                  {
                        break;
                  }
                  Thread.Sleep(500);
                }
            }
      );
    }
}

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static Task task;
    static CancellationTokenSource timespy = new CancellationTokenSource();
    static void Main()
    {
      Task.WaitAll(ShowTimeMain(),MonitorShowTimeStatus());
    }
   
    static async Task ShowTimeMain()
    {
      task = ShowTime();
      try
      {
            await task;            
      }
      catch // 任务取消异常
      {}
    }
   
    static async Task ShowTime()
    {
      await Task.Factory.StartNew
      (
            () =>
            {
                int n = 10;
                while (n >= 0)
                {
                  if (timespy.IsCancellationRequested)
                  {
                        timespy.Token.ThrowIfCancellationRequested();
                  }
                  Console.WriteLine(DateTime.Now);
                  Thread.Sleep(1000);
                  n--;
                }
            }
            , timespy.Token
      );
    }
   
    static async Task MonitorShowTimeStatus()
    {
      if (task == null)
      {
            await Task.Delay(500);
      }
      await Task.Factory.StartNew
      (
            () =>
            {
                int n = 10;
                while (true)
                {
                  n--;
                  Console.WriteLine("{0,-2} {1}", n,task.Status);
                  if (task.Status == TaskStatus.RanToCompletion || task.Status == TaskStatus.Canceled)
                  {
                        break;
                  }
                  Thread.Sleep(500);
                  if (n == 0)
                  {
                        timespy.Cancel();
                  }
                }
            }
      );
    }
}

Cool_Breeze 发表于 2022-5-23 18:47

lzy00 发表于 2022-5-23 10:47
await放while里是起什么作用呢

await 不管放在哪里都会等待 它 等待的任务执行完成后,再去执行await后面的语句。

这里是延时1秒中,在任务没有完成时,它在这个等待的时间内不会占用CPU,CPU可以去做其它的事情

无名大师 发表于 2021-5-4 14:02

学习学习 刚好用用

waiting91 发表于 2021-5-4 15:02

学习学习 刚好用用

town174 发表于 2021-11-13 20:32

不错,建议将task的并发执行,取消等机制也讲下,就更完整了

zxw75192 发表于 2021-11-15 21:29

学习学习

wang65424773 发表于 2021-11-17 12:51

学习了,Thread 在挂起状态怎么直接关闭掉?

Cool_Breeze 发表于 2021-11-17 18:11

wang65424773 发表于 2021-11-17 12:51
学习了,Thread 在挂起状态怎么直接关闭掉?

线程有个abort方法!

alphasong 发表于 2021-11-18 17:36

最好可以讲讲.net最典型的的委托

zzvzz 发表于 2022-5-2 11:08

个人还是比较喜欢看笔记的,毕竟俺也喜欢写笔记
页: [1] 2
查看完整版本: C# 多线程与异步的使用方法笔记