C#——多线程(重点Task)

基本概念:

线程运行的本质是函数的执行

多线程共享的资源是堆区的数据(非局部变量)

当所有前台线程都运行完毕后如果还有后台线程在运行,那么所有的后台线程都会被终止掉

线程优先级:CPU时间分配的倾向性,优先级高的多分配,但不表示执行顺序的必然性

临界资源访问:

  • 死锁,活锁和饥饿

死锁产生的条件:

死锁的解决:

  • 忽略,罕见问题。解决代价大但收益很小的问题
  • 预防,破坏四个条件
  • 避免,银行家算法
  • 检测恢复,检测到杀进程

实现方式:

1、Thread

  • Start
  • 最原始的多线程运作方式
  • 支持object传入参数
  • 同步化,返回结果需要自己实现

2、线程池

  • QueueUserWorkItem
  • ThreadPool上提供对于资源的管控。控制线程数量以及线程开辟和销毁的资源
  • 支持object传入参数

3、委托

  • BeginInvoke
  • 支持明确类型的入参和出参
  • 同步化,Callback和EndInvoke
  • 死锁

4、Task

  • 线程池管理
  • 多种开启方式,TaskFactory、Task、StartNew
  • 支持入参和出参(可以有返回值)
  • 同步化,Continue、Wait、When

Continue 实例:

Task<int> t = new Task<int>(HeavyTaskOR, 5);    //int 返回类型,需要Start
//Task<int> t = new Task<int>(HeavyTaskOR, 5, TaskCreationOptions.LongRunning);    //int 返回类型,需要Start
t.Start();
//Continue相当于回调,就是在Task完成之后执行
t.ContinueWith(r =>
{
    this.Dispatcher.Invoke(() =>
    {
        this.lb.Items.Add(r.Result);
    });
});

WhenAll 实例:

Task<int>[] ts = new Task<int>[5];
for (int i = 0; i < 5; i++)
{
    int j = i;  //解决闭包问题
    ts[i] = Task.Run<int>(() =>
    {
        Thread.Sleep(500);
        return HearyTaskIR(j);  
    });
}

this.lb.Items.Add("main");

Task.WhenAll<int>(ts).ContinueWith(rs =>        //WhenAll 等待线程全部完成后执行
{
    this.Dispatcher.Invoke(() =>
    {
        this.lb.Items.Add(rs.Result[0] + 4);
    });
});

Wait 实例:

//队列Queue,线程安全
list = new ConcurrentQueue<string>();
var ts = new Task[5];
for(int i=0;i<5;i++)
{
    int j = i;
    ts[i] = new Task(() =>
      {
          Thread.Sleep(1000);
          list.Enqueue(j.ToString());
      });
    ts[i].Start();
}
//Wait是阻塞线程,因为我们在UI线程上调用,所以界面会卡死,在后台线程不会,所以要避免在UI线程上调用此方法
Task.WaitAll(ts);
foreach(string item in list.ToList())
{
    this.lb.Items.Add(item);
}

Task 线程的取消,避免强杀线程

//线程的取消,避免强杀线程
//CancellationTokenSource是向应该取消的CancellationToken发送信号
CancellationTokenSource cts = new CancellationTokenSource();    
Task<int> t = new Task<int>(() => HearyTaskORC(5,cts.Token));
t.Start();
cts.Cancel();
this.lb.Items.Add("main");
t.ContinueWith(r =>
{
    this.Dispatcher.Invoke(() =>
    {
        this.lb.Items.Add(r.Result);
    });
});

父任务和子任务实例:

Task<int> tp = new Task<int>(() =>
  {
      Task<int> tc = new Task<int>(() =>
        {
            Thread.Sleep(1000);
            return 1;
        },TaskCreationOptions.AttachedToParent);    //父线程的子线程,属于父线程的一部分

      tc.Start();
      tc.ContinueWith(r =>
      {
          this.Dispatcher.Invoke(() =>
          {
              this.lb.Items.Add(r.Result);
          });
      });
      return 2;
  });

tp.Start();
tp.ContinueWith(r =>
{
    this.Dispatcher.Invoke(() =>
    {
        this.lb.Items.Add(r.Result);
    });
});

5、Thread和Task对比

  • 线程池
  • 出参(Task可以有返回参)
  • 同步化
  • 取消方式(Thread直接杀死线程,Task自己提供查询接口,判断是不是要结束线程)

6、Parallel

  • For,Invoke
  • Break

实例:

private void Parallel_Click(object sender, RoutedEventArgs e)
{
    list = new ConcurrentQueue<string>();
    //开10个线程
    Parallel.For(0, 10, (i, state) =>
      {
          //开到第6个时,不再继续开线程,但有时候可能已经开到了6个或7个,直到能响应此Break之后不再继续开
          if (i > 5)
          {
              state.Break();
          }
          list.Enqueue(i.ToString());
      });
    //Invoke可以传一系列的Action进去,此Action并行执行
    Parallel.Invoke(() => 
    { 
        list.Enqueue("a"); 
    },
        () => {
            list.Enqueue("b");
        });
    foreach(string item in list.ToList())
    {
        this.lb.Items.Add(item);
    }
    
}

7、Async / Await(C#语法糖)

  • 异步编程思维的转变
  • Async函数返回类型只能为void和Task
  • Async中应该要有Await,如果不写Await,编译器会将其变为同步执行函数
  • Await只能等待Task
  • UI问题
  • ConfigureAwait指定是否恢复上下文
  • Wait和Result是同步方法

实例:

private async void testAsync()
{
    Task t = new Task(() =>
     {
         Thread.Sleep(5000);
         this.Dispatcher.Invoke(() =>
         {
             this.lb.Items.Add("task");
         });
     });
    t.Start();

    await t;    //和Task.await区别在于不阻塞UI线程
    this.lb.Items.Add("main");
    /*
    //Result是将UI线程占用,等待结果,await运行完成后会回到UI,由于UI线程一直被占用,所以无法回到UI,await会一直等待,所以产生了死锁
    // this.lb.Items.Add(LoadStringAsync().Result);
    string s = await LoadStringAsync();
    this.lb.Items.Add(s);
    */
}
private async Task<string>LoadStringAsync()
{
    Task<string> t1 = new Task<string>(() =>
       {
           Thread.Sleep(3000);
           return "first";
       });
    t1.Start();
    Task<string> t2 = new Task<string>(() =>
      {
          Thread.Sleep(3000);
          return "second";
      });
    t2.Start();
    string first = await t1;
    string second = await t2;
    return first + " " + second;
}

8、Timer

  • 定时任务,省去控制时间的循环
  • DispatcherTimer可直接访问UI
  • 被销毁问题

9、Dispatcher

  • BeginInvoke(异步调用),Invoke(同步调用)
  • UI主线程调用
  • DispatcherPriority

发展进程:

  • Thread
  • ThreadPool
  • Task
  • Async/Await

祝大家早日成为技术大咖~

posted @ 2022-09-27 14:41  Echo_Lee0823  阅读(1703)  评论(0编辑  收藏  举报