C# -- 多线程

一、多线程的根源

定义:C#/.Net Thread ThreadPool Task await/async -- C# 是面向对象的语音,

Thread 就是一个封装,封装的计算机线程概念(一些 API)。

Thread.CurrentThread.ManagedThreadId:当前响应线程的 ID,没有什么意义,是 CLR 起的标识,不是系统的。

1、进程 Processor:

属于计算机概念(虚拟的),把一个程序运行时占用的全部计算资源(CPU、内存、磁盘、网络...),统称为进程。

2、线程 Thread:

也是计算机概念(虚拟的),是进程的最小执行流(任何一个操作的响应都需要执行流),线程也有自己的计算资源 -- 线程是依托于进程的

3、多线程 MultiThread:

多个执行流并发执行,就说多线程。

二、单线程与多线程

1、单线程卡界面,多线程不卡界面

2、单线程慢,多线程快

3、多线程无序性

 

单线程:

Debug.WriteLine(" ");
Debug.WriteLine("******** btnSingleThread_Click 同步方法 start {0}*******", Thread.CurrentThread.ManagedThreadId);


for (int i = 0; i < 5; i++)
{
    string name = $"btnSingleThread_Click_{i}";

    Action action = () => { this.DoSomethingLong(name); };
    action.Invoke();//同步执行。
}

Debug.WriteLine("******** btnSingleThread_Click 同步方法 end {0}*******", Thread.CurrentThread.ManagedThreadId);
Debug.WriteLine(" ");

多线程:

Debug.WriteLine(" ");
Debug.WriteLine("******** btnMulti_Click 同步方法 start {0}*******", Thread.CurrentThread.ManagedThreadId);


for (int i = 0; i < 10; i++)
{
    string name = $"btnMulti_Click_{i}";

    Action action = () => { this.DoSomethingLong(name); };
    //action.Invoke();//同步执行。

    Task.Run(action);
}

Debug.WriteLine("******** btnMulti_Click_ 同步方法 end {0}*******", Thread.CurrentThread.ManagedThreadId);
Debug.WriteLine(" ");

Q:单线程卡界面,多线程不卡界面

A1:线程就是执行流,卡界面就表示线程正在忙碌,忙于 DoSomething ,所以界面卡住了 UI 线程(主线程)。

A2:UI 线程闲置了,DoSomething 交给了子线程(其他线程 Task.Run)。

应用:发短信,支付,上传文件。直接用子线程完成,主线程先返回。

 

Q:单线程慢,多线程快

A1:多线程是多个人干活,但需要更多的资源,多线程本质是资源换性能。

线程数不成正比

CPU 不够 -- CPU 只有这么多,线程多了是没有意义的。

调度机制 -- 线程切换。CPU 分片、线程调度、上下文切换都需要成本。

 

注意:线程不是越多越好。Task 比较好,是基于 ThreadPoll 开发的。线程的数量在 CPU 核数*3 (默认线程池有 2048个线程)以内比较合适。

应用:需要提升处理速度的地方可以考虑多线程。

 

多线程无序性:

因为线程是计算机资源,.Net 无法控制。因为 CPU 调度策略,启动与完成时是无序的。

 

三、多线程的写法

任何的多线程,都离不开委托 delegate,最佳实现是 Task 写法。

委托:是同步执行的

多线程:多个执行流,多线程并发。

 

1、Thread

2、ThreadPool

3、BeginInvoke

4、Task

5、Parallel

6、await/async

 

四、Task

1、Task 多线从场景实战

2、线程顺序掌握,多 API 灵活应用

任务能拆分、能并行的才可以多线程。

阻塞式:卡 UI

Debug.WriteLine(" ");
Debug.WriteLine("******** btnAdvanced_Click start {0}*******", Thread.CurrentThread.ManagedThreadId);

Debug.WriteLine("准备 1");
Debug.WriteLine("准备 2");
Debug.WriteLine("准备 3");
Debug.WriteLine("准备 4");
Debug.WriteLine("准备 5");

List<Task> taskList = new List<Task>();
taskList.Add(Task.Run(() => { Coding("Leo", "SQLServer"); }));
taskList.Add(Task.Run(() => { Coding("Chaw", "C#"); }));
taskList.Add(Task.Run(() => { Coding("Joanna", "Java"); }));
taskList.Add(Task.Run(() => { Coding("Bob", "WeChat"); }));

Task.WaitAny(taskList.ToArray());//阻塞式:等待 tasklist 中任一个完成后,会造成 UI 卡死。因为主线程在等待

//需要 tasklist 完成后才能执行
Task.WaitAll(taskList.ToArray());//阻塞式:等待 tasklist 完成后,会造成 UI 卡死。因为主线程在等待
Debug.WriteLine("完成交钱 5");

Debug.WriteLine(" ");
Debug.WriteLine("******** btnAdvanced_Click end {0}*******", Thread.CurrentThread.ManagedThreadId);

非阻塞式:不卡 UI,但能按顺序完成。

再起一个 Task.Run,把阻塞任务放在其他线程,所以主线程 UI,不会卡。【不推荐,不要乱包】

List<Task> taskList = new List<Task>();
taskList.Add(Task.Run(() => { Coding("Leo", "SQLServer"); }));
taskList.Add(Task.Run(() => { Coding("Chaw", "C#"); }));
taskList.Add(Task.Run(() => { Coding("Joanna", "Java"); }));
taskList.Add(Task.Run(() => { Coding("Bob", "WeChat"); }));

//把阻塞等待任务交给其他线程
Task.Run(() =>
{
    Task.WaitAny(taskList.ToArray());//阻塞式:等待 tasklist 中任一个完成后,会造成 UI 卡死。因为主线程在等待

    Debug.WriteLine("完成交钱 30%");

    //需要 tasklist 完成后才能执行
    Task.WaitAll(taskList.ToArray());//阻塞式:等待 tasklist 完成后,会造成 UI 卡死。因为主线程在等待

    Debug.WriteLine("完成交钱 50%");
});

Debug.WriteLine(" ");
Debug.WriteLine("******** btnAdvanced_Click end {0}*******", Thread.CurrentThread.ManagedThreadId);

推荐使用此方法解决阻塞问题:

List<Task> taskList = new List<Task>();
taskList.Add(Task.Run(() => { Coding("Leo", "SQLServer"); }));
taskList.Add(Task.Run(() => { Coding("Chaw", "C#"); }));
taskList.Add(Task.Run(() => { Coding("Joanna", "Java"); }));
taskList.Add(Task.Run(() => { Coding("Bob", "WeChat"); }));


taskList[0].ContinueWith(t => Debug.WriteLine("Leo 完成"));
Task.Factory.ContinueWhenAny(taskList.ToArray(), t => Debug.WriteLine("有一个完成"));
Task.Factory.ContinueWhenAll(taskList.ToArray(), t => Debug.WriteLine("全部完成"));

3、临时变量 - 线程异常 - 线程取消

a、临时变量:

for (int i = 0; i < 10; i++)
{
    string name = $"btnMulti_Click_{i}";

    //Action action = () => { this.DoSomethingLong(name); };

    Action action = () => { this.DoSomethingLong($"btnMulti_Click_{i}"); };
    //action.Invoke();//同步执行。

    Task.Run(action);
}

多线程问题:以下两者是不一样的。因为执行时机与上下文的问题。

前者:先计算出了 i 后才开始执行线程,使用的是 name 参数,已经绑定好了。

后者:线程执行时,才调用代入 i,此时 i 是变化的。

Action action = () => { this.DoSomethingLong(name); };

Action action = () => { this.DoSomethingLong($"btnMulti_Click_{i}"); };

4、多方案线程安全和任务分配

均匀的分配任务:

当线程超过 27 ,等有任务完成再分配任务。

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值