Threads(异步和多线程)
Task是.NET Framework4.5出现的,线程是基于线程池的,然后提供丰富的api,Thread方法很多很强大,但是太过强大,没有限制。
DoSomethingLong方法如下:
/// <summary>
/// 一个比较耗时耗资源的私有方法
/// </summary>
/// <param name="name"></param>
private void DoSomethingLong(string name)
{
Console.WriteLine($"****************DoSomethingLong Start {name} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = ;
for (int i = ; i < 1_000_000_000; i++)
{
lResult += i;
}
Thread.Sleep(); Console.WriteLine($"****************DoSomethingLong End {name} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
Task的使用:
{
Task task = new Task(() => this.DoSomethingLong("btnTask_Click_1"));
task.Start();
}
{
Task task = Task.Run(() => this.DoSomethingLong("btnTask_Click_2"));
}
{
TaskFactory taskFactory = Task.Factory;
Task task = taskFactory.StartNew(() => this.DoSomethingLong("btnTask_Click_3"));
}
如果这样去调用:
ThreadPool.SetMaxThreads(, );
for (int i = ; i < ; i++)
{
int k = i;
Task.Run(() =>
{
Console.WriteLine($"This is {k} running ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
Thread.Sleep();
});
}
如果去掉设置最大线程的代码:
for (int i = ; i < ; i++)
{
int k = i;
Task.Run(() =>
{
Console.WriteLine($"This is {k} running ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
Thread.Sleep();
});
}
运行结果如下:
ThreadPool.SetMaxThreads(8, 8);
线程池是单例的,全局唯一的,设置后,同时并发的Task只有8个,而且是复用的,Task的线程是源于线程池的,全局的,请不要这样设置。
假如我想控制下Task的并发数量,改怎么做?
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine("在Sleep之前");
Thread.Sleep();//同步等待--当前线程等待2s 然后继续
Console.WriteLine("在Sleep之后");
stopwatch.Stop();
Console.WriteLine($"Sleep耗时{stopwatch.ElapsedMilliseconds}");
}
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine("在Delay之前");
Task task = Task.Delay()
.ContinueWith(t =>
{
stopwatch.Stop();
Console.WriteLine($"Delay耗时{stopwatch.ElapsedMilliseconds}"); Console.WriteLine($"This is ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
});//异步等待--等待2s后启动新任务
Console.WriteLine("在Delay之后");
stopwatch.Stop();
Console.WriteLine($"Delay耗时{stopwatch.ElapsedMilliseconds}");
}
运行结果如下:
如果将最后一个stopwatch注释掉:
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine("在Sleep之前");
Thread.Sleep();//同步等待--当前线程等待2s 然后继续
Console.WriteLine("在Sleep之后");
stopwatch.Stop();
Console.WriteLine($"Sleep耗时{stopwatch.ElapsedMilliseconds}");
}
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine("在Delay之前");
Task task = Task.Delay()
.ContinueWith(t =>
{
stopwatch.Stop();
Console.WriteLine($"Delay耗时{stopwatch.ElapsedMilliseconds}"); Console.WriteLine($"This is ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
});//异步等待--等待2s后启动新任务
Console.WriteLine("在Delay之后");
//stopwatch.Stop();
//Console.WriteLine($"Delay耗时{stopwatch.ElapsedMilliseconds}");
}
什么时候用多线程?
任务并发是时候
多线程能干嘛?
提升速度,优化用户体验。
比如,现在有一个场景,在公司开会,领导在分配任务,不能并发,因为只能有一个领导在讲话分配任务,当任务分配下去,开发们确实可以同时开始撸代码,这个是可以并发的。
TaskFactory taskFactory = new TaskFactory();
List<Task> taskList = new List<Task>();
taskList.Add(taskFactory.StartNew(() => this.Coding("bingle1", "Portal")));
taskList.Add(taskFactory.StartNew(() => this.Coding("bingle2", " DBA ")));
taskList.Add(taskFactory.StartNew(() => this.Coding("bingle3", "Client")));
taskList.Add(taskFactory.StartNew(() => this.Coding("bingle4", "BackService")));
taskList.Add(taskFactory.StartNew(() => this.Coding("bingle5", "Wechat")));
现在要求,谁第一个完成,获得红包奖励(ContinueWhenAny);所有完成后,一起庆祝下(ContinueWhenAll),将其放入一个List<Task>里面去
TaskFactory taskFactory = new TaskFactory();
List<Task> taskList = new List<Task>();
taskList.Add(taskFactory.StartNew(() => this.Coding("bingle1", "Portal")));
taskList.Add(taskFactory.StartNew(() => this.Coding("bingle2", " DBA ")));
taskList.Add(taskFactory.StartNew(() => this.Coding("bingle3", "Client")));
taskList.Add(taskFactory.StartNew(() => this.Coding("bingle4", "BackService")));
taskList.Add(taskFactory.StartNew(() => this.Coding("bingle5", "Wechat"))); //谁第一个完成,获取一个红包奖励
taskFactory.ContinueWhenAny(taskList.ToArray(), t => Console.WriteLine($"XXX开发完成,获取个红包奖励{Thread.CurrentThread.ManagedThreadId.ToString("")}"));
//项目完成后,一起庆祝一下
taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), rArray => Console.WriteLine($"开发都完成,一起庆祝一下{Thread.CurrentThread.ManagedThreadId.ToString("")}")));
ContinueWhenAny ContinueWhenAll 非阻塞式的回调;而且使用的线程可能是新线程,也可能是刚完成任务的线程,唯一不可能是主线程
//阻塞当前线程,等着任意一个任务完成
Task.WaitAny(taskList.ToArray());//也可以限时等待
Console.WriteLine("准备环境开始部署");
//需要能够等待全部线程完成任务再继续 阻塞当前线程,等着全部任务完成
Task.WaitAll(taskList.ToArray());
Console.WriteLine("5个模块全部完成后,集中点评");
Task.WaitAny WaitAll都是阻塞当前线程,等任务完成后执行操作,阻塞卡界面,是为了并发以及顺序控制,网站首页:A数据库 B接口 C分布式服务 D搜索引擎,适合多线程并发,都完成后才能返回给用户,需要等待WaitAll,列表页:核心数据可能来自数据库/接口服务/分布式搜索引擎/缓存,多线程并发请求,哪个先完成就用哪个结果,其他的就不管了。
假如说我想控制下Task的并发数量,该怎么做? 20个
List<Task> taskList = new List<Task>();
for (int i = ; i < ; i++)
{
int k = i;
if (taskList.Count(t => t.Status != TaskStatus.RanToCompletion) >= )
{
Task.WaitAny(taskList.ToArray());
taskList = taskList.Where(t => t.Status != TaskStatus.RanToCompletion).ToList();
}
taskList.Add(Task.Run(() =>
{
Console.WriteLine($"This is {k} running ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
Thread.Sleep();
}));
}
Parallel并发执行多个Action线程,主线程会参与计算---阻塞界面。等于TaskWaitAll+主线程计算
Parallel.Invoke(() => this.DoSomethingLong("btnParallel_Click_1"),
() => this.DoSomethingLong("btnParallel_Click_2"),
() => this.DoSomethingLong("btnParallel_Click_3"),
() => this.DoSomethingLong("btnParallel_Click_4"),
() => this.DoSomethingLong("btnParallel_Click_5"));
Parallel.For(, , i => this.DoSomethingLong($"btnParallel_Click_{i}"));
Parallel.ForEach(new int[] { , , , , }, i => this.DoSomethingLong($"btnParallel_Click_{i}")); ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = ;
Parallel.For(, , options, i => this.DoSomethingLong($"btnParallel_Click_{i}"));
有没有办法不阻塞?
Task.Run(() =>
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = ;
Parallel.For(, , options, i => this.DoSomethingLong($"btnParallel_Click_{i}"));
});
几乎90%以上的多线程场景,以及顺序控制,以上的Task的方法就可以完成,如果你的多线程场景太复杂搞不定,那么请梳理一下你的流程,简化一下。建议最好不要线程嵌套线程,两三次勉强能懂,三层就hold不住了,更多的只能求神。
多线程异常:
try
{ List<Task> taskList = new List<Task>();
for (int i = ; i < ; i++)
{
string name = $"btnThreadCore_Click_{i}";
taskList.Add(Task.Run(() =>
{
if (name.Equals("btnThreadCore_Click_11"))
{
throw new Exception("btnThreadCore_Click_11异常");
}
else if (name.Equals("btnThreadCore_Click_12"))
{
throw new Exception("btnThreadCore_Click_12异常");
}
else if (name.Equals("btnThreadCore_Click_38"))
{
throw new Exception("btnThreadCore_Click_38异常");
}
Console.WriteLine($"This is {name}成功 ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
}));
}
//多线程里面抛出的异常,会终结当前线程;但是不会影响别的线程;
//那线程异常哪里去了? 被吞了,
//假如我想获取异常信息,还需要通知别的线程
Task.WaitAll(taskList.ToArray());//1 可以捕获到线程的异常
}
catch (AggregateException aex)//2 需要try-catch-AggregateException
{
foreach (var exception in aex.InnerExceptions)
{
Console.WriteLine(exception.Message);
}
}
catch (Exception ex)//可以多catch 先具体再全部
{
Console.WriteLine(ex);
}
//线程异常后经常是需要通知别的线程,而不是等到WaitAll,问题就是要线程取消
//工作中常规建议:多线程的委托里面不允许异常,包一层try-catch,然后记录下来异常信息,完成需要的操作
线程取消:
//多线程并发任务,某个失败后,希望通知别的线程,都停下来,how?
//Thread.Abort--终止线程;向当前线程抛一个异常然后终结任务;线程属于OS资源,可能不会立即停下来
//Task不能外部终止任务,只能自己终止自己(上帝才能打败自己) //cts有个bool属性IsCancellationRequested 初始化是false
//调用Cancel方法后变成true(不能再变回去),可以重复cancel
try
{
CancellationTokenSource cts = new CancellationTokenSource();
List<Task> taskList = new List<Task>();
for (int i = ; i < ; i++)
{
string name = $"btnThreadCore_Click_{i}";
taskList.Add(Task.Run(() =>
{
try
{
if (!cts.IsCancellationRequested)
Console.WriteLine($"This is {name} 开始 ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}"); Thread.Sleep(new Random().Next(, )); if (name.Equals("btnThreadCore_Click_11"))
{
throw new Exception("btnThreadCore_Click_11异常");
}
else if (name.Equals("btnThreadCore_Click_12"))
{
throw new Exception("btnThreadCore_Click_12异常");
}
else if (name.Equals("btnThreadCore_Click_13"))
{
cts.Cancel();
}
if (!cts.IsCancellationRequested)
{
Console.WriteLine($"This is {name}成功结束 ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
}
else
{
Console.WriteLine($"This is {name}中途停止 ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
return;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
cts.Cancel();
}
}, cts.Token));
}
//1 准备cts 2 try-catch-cancel 3 Action要随时判断IsCancellationRequested
//尽快停止,肯定有延迟,在判断环节才会结束 Task.WaitAll(taskList.ToArray());
//如果线程还没启动,能不能就别启动了?
//1 启动线程传递Token 2 异常抓取
//在Cancel时还没有启动的任务,就不启动了;也是抛异常,cts.Token.ThrowIfCancellationRequested
}
catch (AggregateException aex)
{
foreach (var exception in aex.InnerExceptions)
{
Console.WriteLine(exception.Message);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
临时变量:
for (int i = ; i < ; i++)
{
Task.Run(() =>
{
Console.WriteLine($"This is btnThreadCore_Click_{i} ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
});
}
为什么运行结果后,都是5呢?
临时变量问题,线程是非阻塞的,延迟启动的;线程执行的时候,i已经是5了
那么该如何解决呢?
每次都声明一个变量k去接收,k是闭包里面的变量,每次循环都有一个独立的k,5个k变量 1个i变量
for (int i = ; i < ; i++)
{
int k = i;
Task.Run(() =>
{
Console.WriteLine($"This is btnThreadCore_Click_{i}_{k} ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
});
}
这样再运行,结果就正常了。
线程安全&lock:
线程安全:如果你的代码在进程中有多个线程同时运行这一段,如果每次运行的结果都跟单线程运行时的结果一致,那么就是线程安全的
线程安全问题一般都是有全局变量/共享变量/静态变量/硬盘文件/数据库的值,只要多线程都能访问和修改
发生是因为多个线程相同操作,出现了覆盖,怎么解决?
1 Lock解决多线程冲突
Lock是语法糖,Monitor.Enter,占据一个引用,别的线程就只能等着
推荐锁是private static readonly object,
A不能是Null,可以编译不能运行;
B 不推荐lock(this),外面如果也要用实例,就冲突了
//Test test = new Test();
//Task.Delay(1000).ContinueWith(t =>
//{
// lock (test)
// {
// Console.WriteLine("*********Start**********");
// Thread.Sleep(5000);
// Console.WriteLine("*********End**********");
// }
//});
//test.DoTest(); //C 不应该是string; string在内存分配上是重用的,会冲突
//D Lock里面的代码不要太多,这里是单线程的
Test test = new Test();
string student = "水煮鱼";
Task.Delay().ContinueWith(t =>
{
lock (student)
{
Console.WriteLine("*********Start**********");
Thread.Sleep();
Console.WriteLine("*********End**********");
}
});
test.DoTestString();
//2 线程安全集合
//System.Collections.Concurrent.ConcurrentQueue<int> //3 数据分拆,避免多线程操作同一个数据;又安全又高效 for (int i = ; i < ; i++)
{
this.iNumSync++;
}
for (int i = ; i < ; i++)
{
Task.Run(() =>
{
lock (Form_Lock)//任意时刻只有一个线程能进入方法块儿,这不就变成了单线程
{
this.iNumAsync++;
}
});
}
for (int i = ; i < ; i++)
{
int k = i;
Task.Run(() => this.iListAsync.Add(k));
} Thread.Sleep( * );
Console.WriteLine($"iNumSync={this.iNumSync} iNumAsync={this.iNumAsync} listNum={this.iListAsync.Count}");
//iNumSync 和 iNumAsync分别是多少 9981/9988 1到10000以内
Threads(异步和多线程)的更多相关文章
- IOS异步和多线程操作&&在sqlite3中的应用
1,数据库I/O操作(异步) 数据库本身是存储在磁盘上.访问和修改数据库,即对磁盘进行读写,即I/O操作. 磁盘属于计算机硬件,具有DMA能力,不需要CPU干预,可以实现异步操作. I/O操作一般是消 ...
- C#中异步和多线程的区别
C#中异步和多线程的区别是什么呢?异步和多线程两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性.甚至有些时候我们就认为异步和多线程是等同的概念.但是,异步和多线程还是有一些区别的.而这些区 ...
- PHP中实现异步调用多线程程序代码
本文章详细的介绍了关于PHP中实现异步调用多线程方法,下面我们以给1000个用户发送一封推荐邮件,用户输入或者导入邮件账号了提交服务器执行发送来讲述. 比如现在有一个场景,给1000个用户发送一封推荐 ...
- c#异步和多线程有什么区别和联系?
异步和多线程可以说没有必然的联系,只能说异步可以通过多线程实现而已要理解这些东西,你得具备很多相关的知识,操作系统原理,编译原理等简单地来说,计算机或者说CPU执行你的代码都是顺序执行的,当前的语句没 ...
- 编写高质量代码改善C#程序的157个建议——建议71:区分异步和多线程应用场景
建议71:区分异步和多线程应用场景 初学者有时候会将异步和多线程混为一谈.如果对它们之间的区别不是很清楚,很容易写出下面这样的代码: private void buttonGetPage_Click( ...
- C#用委托实现异步,异步与多线程的异同
异步与多线程的区别(转) 一.异步和多线程有什么区别?其实,异步是目的,而多线程是实现这个目的的方法.异步是说,A发起一个操作后(一般都是比较耗时的操作,如果不耗时的操作就没有必要异步了),可以继续自 ...
- 【Java_基础】并发、并行、同步、异步、多线程的区别
1. 并发:位于同一个处理器上的多个已开启未完成的线程,在任意一时刻系统调度只能让一个线程获得CPU资源运行,虽然这种调度机制有多种形式(大多数是以时间片轮巡为主).但无论如何,都是通过不断切换需要运 ...
- C#中的异步和多线程
许多开发人员对异步代码和多线程以及它们的工作原理和使用方法都有错误的认识.在这里,你将了解这两个概念之间的区别,并使用c#实现它们. 我:"服务员,这是我第一次来这家餐厅.通常需要4个小时才 ...
- C#异步和多线程以及Thread、ThreadPool、Task区别和使用方法
本文的目的是为了让大家了解什么是异步?什么是多线程?如何实现多线程?对于当前C#当中三种实现多线程的方法如何实现和使用?什么情景下选用哪一技术更好? 第一部分主要介绍在C#中异步(async/awai ...
随机推荐
- SpringCloud微服务(05):Zuul组件,实现路由网关控制
本文源码:GitHub·点这里 || GitEE·点这里 一.Zuul组件简介 1.基础概念 Zuul 网关主要提供动态路由,监控,弹性,安全管控等功能.在分布式的微服务系统中,系统被拆为了多个微服务 ...
- .net core 中间件使用
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; usi ...
- java基础(2):注释、关键字、标识符、数据
1. 注释.关键字与标识符 大家可以安装一个文本编辑软件notepad++,有利于java代码的查看与编写. 1.1 程序注释 通常我们需要在源代码中添加文字用来对进行代码解释说明,但这些文字并不是J ...
- js-事件函数调用简化
// 一般写法 function fn(event) { console.log(event) } div.onclick = function (event) { fn(event) } ===== ...
- JS基础语法---(数据)简单类型和复杂类型
原始数据类型: number, string, boolean, undefined, null, object 基本类型(简单类型), 即值类型: number, string, boolean 复 ...
- HTML 本地存储
HTML 本地存储:优于 cookies. 什么是 HTML 本地存储? 通过本地存储(Local Storage),web 应用程序能够在用户浏览器中对数据进行本地的存储. 在 HTML5 之前,应 ...
- 简单快速上手Jackson使用
1简介 Jackson具有比较高的序列化和反序列化效率,据测试,无论是哪种形式的转换,Jackson > Gson > Json-lib,而且Jackson的处理能力甚至高出Json-li ...
- Python 定时任务的实现方式
本文转载自: https://lz5z.com/Python%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E7%9A%84%E5%AE%9E%E7%8E%B0%E6%96% ...
- kettle教程---通过配置表格配置实现数据的批量增量更新(实用)
本文接上篇文章,上面文章讲的是,通过配置文件的全量更新,现在说下增量更新 如上图所示,涉及到1个转换和1个作业. 1-表增量同步(转换) 可以通过读取同步表参数这个excel表格文件,获取表名称和同步 ...
- JS---DOM---为元素解除绑定事件
解除绑定事件: 1.解绑事件 对象 .on 事件名字=事件处理函数--->绑定事件. 对象 .on 事件名字 = null . 注意:用什么方式绑定事件,就应该用对应的方式解除绑定事件. //1 ...