那么什么时候能用多线程? 任务能并发的时候

多线程能干嘛?提升速度/优化用户体验

网站首页:A数据库 B接口 C分布式服务 D搜索引擎,适合多线程并发,都完成后才能返回给用户,需要等待WaitAll
列表页:核心数据可能来自数据库/接口服务/分布式搜索引擎/缓存,多线程并发请求,哪个先完成就用哪个结果,其他的就不管了

现实实例

多人合作开发---多线程--提升效率/性能

               {
TaskFactory taskFactory = new TaskFactory();
List<Task> taskList = new List<Task>();
taskList.Add(taskFactory.StartNew(o=> Coding("A", " Portal"), "A"));
taskList.Add(taskFactory.StartNew(o=> Coding("B", " DBA"), "B"));
taskList.Add(taskFactory.StartNew(o=> Coding("C", " Client"), "C"));
taskList.Add(taskFactory.StartNew(o=> Coding("D", "Service"), "D"));
taskList.Add(taskFactory.StartNew(o=> Coding("E", " Wechat"), "E")); //谁第一个完成,获取一个红包奖励
taskFactory.ContinueWhenAny(taskList.ToArray(), t => Console.WriteLine($"{t.AsyncState}开发完成,获取个红包奖励{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都是阻塞当前线程,等任务完成后执行操作
//阻塞卡界面,是为了并发以及顺序控制
}
         /// <summary>
/// 模拟Coding过程
/// </summary>
/// <param name="name"></param>
/// <param name="projectName"></param>
private static string Coding(string name, string projectName)
{
Console.WriteLine($"****************Coding Start {name} {projectName} {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;
}
Console.WriteLine($"****************Coding End {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
return name;
}

多线程异常处理

             #region 多线程异常处理
{
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,然后记录下来异常信息,完成需要的操作
}
#endregion

多线程里面抛出的异常,会终结当前线程;但是不会影响别的线程;线程异常哪里去了? 被吞了

多线程的委托里面不允许异常,包一层try-catch,然后记录下来异常信息 ,通知别的线程

线程取消

               {
CancellationTokenSource cts = new CancellationTokenSource();
var token = cts.Token; cts.Cancel();
CancellationTokenSource cts2 = new CancellationTokenSource();
var token2 = cts2.Token;
List<Task> taskList = new List<Task>();
for (int i = ; i < ; i++)
{
int k = i;
switch (i%)
{
case :
taskList.Add(Task.Run(() => { Console.WriteLine($"i={i},k={k},i%5=0"); }));break;
case :
taskList.Add(Task.Run(() => { Console.WriteLine($"i={i},k={k},i%5=1"); },token)); break;
case :
taskList.Add(Task.Run(() => { Console.WriteLine($"i={i},k={k},i%5=2"); }, token2)); break;
case :
taskList.Add(Task.Run(() => { Console.WriteLine($"i={i},k={k},i%5=3"); })); break;
case :
taskList.Add(Task.Run(() => { Console.WriteLine($"i={i},k={k},i%5=4");
throw new Exception("throw new Exception");
})); break;
}
}
//Thread.Sleep(500);
cts2.Cancel();
try
{
Task.WaitAll(taskList.ToArray());
}catch(AggregateException ae)
{
foreach (var item in ae.InnerExceptions)
{
Console.WriteLine($"{item.GetType().Name}:{item.Message}");
}
}
Console.WriteLine("**********************************");
foreach (var item in taskList)
{
Console.WriteLine($"Id:{item.Id},Status:{item.Status}");
if (item.Exception != null)
{
foreach (var ex in item.Exception.InnerExceptions)
{
Console.WriteLine($"{ex.GetType().Name}:{ex.Message}");
}
}
}
}

运行上面的代码,有四个任务被取消,取消注释,则有两个任务被取消

线程安全

如果你的代码在进程中有多个线程同时运行这一段,如果每次运行的结果都跟单线程运行时的结果一致,那么就是线程安全的
线程安全问题一般都是有全局变量/共享变量/静态变量/硬盘文件/数据库的值,只要多线程都能访问和修改

Lock

1、Lock解决多线程冲突

Lock是语法糖,Monitor.Enter,占据一个引用,别的线程就只能等着

推荐锁是private static readonly object

A 不能是Null,可以编译不能运行;

B 不推荐lock(this),外面如果也要用实例,就冲突了

     public class LockHelper
{
public void Show()
{
LockTest test = new LockTest();
Console.WriteLine(DateTime.Now);
Task.Delay().ContinueWith(t =>
{
lock (test)
{
Console.WriteLine($"*********Start {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}********");
Thread.Sleep();
Console.WriteLine($"*********End {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}********");
}
});
test.LockThis();
}
}
public class LockTest
{
private int lockthis;
public void LockThis()
{
lock (this)
//递归调用,lock this 会不会死锁? 不会死锁!
//这里是同一个线程,这个引用就是被这个线程所占据
{
Thread.Sleep();
this.lockthis++;
if (this.lockthis < )
this.LockThis();
else
Console.WriteLine($"This is {nameof(LockThis)}:{this.lockthis} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
}
}
}

这里LockThis自身递归调用不会死锁,这个引用被当前线程占用,但当另外的实例要使用时就冲突了,必须等待LockThis执行完成后,释放当前实例,外面的实例才能被调用

C 不应该是string; string在内存分配上是重用的,会冲突

             {
LockTest test = new LockTest();
Console.WriteLine(DateTime.Now);
string lockString = "lockString";
Task.Delay().ContinueWith(t =>
{
lock (lockString)
{
Console.WriteLine($"****lockString Start {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}********");
Thread.Sleep();
Console.WriteLine($"****lockString End {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}********");
}
});
test.LockString();
}
public class LockTest
{
private int lockthis;
public void LockThis()
{
lock (this)
//递归调用,lock this 会不会死锁? 不会死锁!
//这里是同一个线程,这个引用就是被这个线程所占据
{
Thread.Sleep();
this.lockthis++;
if (this.lockthis < )
this.LockThis();
else
Console.WriteLine($"This is {nameof(LockThis)}:{this.lockthis} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
}
} private string lockString= "lockString";
public void LockString()
{
lock (lockString)
{
Thread.Sleep();
Console.WriteLine($"This is {nameof(LockString)}:{this.lockString} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
Thread.Sleep();
}
}
}

String类型在内存分配上按享元模式设计的,某个字符串被占用,其他线程就必须等待字符串释放后才能使用

D Lock里面的代码不要太多,这里是单线程的

2、线程安全集合

System.Collections.Concurrent.ConcurrentQueue<T>

3、 数据分拆,避免多线程操作同一个数据;又安全又高效

         private int _sync = ;
private int _async = ;
private List<int> listInt = new List<int>();
private static readonly object lockObject = new object();
public void LockObject()
{
for (int i = ; i < ; i++)
{
this._sync++;
}
for (int i = ; i < ; i++)
{
Task.Run(() => this._async++);
}
for (int i = ; i < ; i++)
{
int k = i;
Task.Run(() => this.listInt.Add(k));
}
Thread.Sleep( * );
Console.WriteLine($"_sync={this._sync} _async={this._async} listInt={this.listInt.Count}");
}

运行上面的代码发现_sync=1000  _async与listInt集合个数都少于1000

         public void LockObject()
{
for (int i = ; i < ; i++)
{
this._sync++;
}
for (int i = ; i < ; i++)
{
Task.Run(() => {
lock (lockObject)
{
this._async++;
}
});
}
for (int i = ; i < ; i++)
{
int k = i;
Task.Run(() => {
lock (lockObject)
{
this.listInt.Add(k);
}
});
}
Thread.Sleep( * );
Console.WriteLine($"_sync={this._sync} _async={this._async} listInt={this.listInt.Count}");
}

使用lock包装后 _async与listInt集合个数都为1000, 使用lock后 只有一个线程才能进入lock方法块内,相当于把程序又变回了单线程

微软文档:

lock:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/lock-statement

CancellationTokenSource:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.cancellationtokensource?view=netframework-4.8

CancellationToken:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.cancellationtoken?view=netframework-4.8

C# 多线程总结 异常处理 线程取消 锁(lock)的更多相关文章

  1. python线程互斥锁Lock(29)

    在前一篇文章 python线程创建和传参 中我们介绍了关于python线程的一些简单函数使用和线程的参数传递,使用多线程可以同时执行多个任务,提高开发效率,但是在实际开发中往往我们会碰到线程同步问题, ...

  2. Python多线程中阻塞(join)与锁(Lock)的使用误区

    参考资料:https://blog.csdn.net/cd_xuyue/article/details/52052893 1使用两个循环分别处理start和join函数.即可实现并发. threads ...

  3. 线程高级篇-Lock锁和Condition条件

    浅谈Synchronized: synchronized是Java的一个关键字,也就是Java语言内置的特性,如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,执行代码块时,其 ...

  4. 多线程的异常处理、线程取消、临时变量、lock

    异步多线程的异常,抓不到,因为是在子线程执行. #region 多线程的异常处理.线程取消.临时变量.lock { try { List<Task> list = new List< ...

  5. .NET 异步多线程,Thread,ThreadPool,Task,Parallel,异常处理,线程取消

    今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主,毕竟用的比较多的现在就是这些了,再往前去的,除非是老项目,不然真的应该是挺少了,大概有个概念,就当了解一 ...

  6. .NET异步多线程,Thread,ThreadPool,Task,Parallel,异常处理,线程取消

    今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主,毕竟用的比较多的现在就是这些了,再往前去的,除非是老项目,不然真的应该是挺少了,大概有个概念,就当了解一 ...

  7. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  8. python笔记9 线程进程 threading多线程模块 GIL锁 multiprocessing多进程模块 同步锁Lock 队列queue IO模型

    线程与进程 进程 进程就是一个程序在一个数据集上的一次动态执行过程.进程一般由程序.数据集.进程控制块三部分组成.我们编写的程序用来描述进程要完成哪些功能以及如何完成:数据集则是程序在执行过程中所需要 ...

  9. Android多线程研究(9)——线程锁Lock

    在前面我们在解决线程同步问题的时候使用了synchronized关键字,今天我们来看看Java 5.0以后提供的线程锁Lock. Lock接口的实现类提供了比使用synchronized关键字更加灵活 ...

随机推荐

  1. Java 从入门到进阶之路(十)

    之前的文章我们介绍了一下 Java 中的引用型数组类型,接下来我们再来看一下 Java 中的继承. 继承的概念 继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类. 继承就是子类继 ...

  2. docker下安装测试环境estuntest

    1.基础知识: docker pull centos   //从云上下载centos系统到本地服务器 docker images   //查看镜像docker rmi 镜像id   //删除镜像 do ...

  3. element table 先显示暂无数据 之后再加载数据 问题

    项目中的表格请求数据时,进去页面,先出现 ''暂无数据'' 字样闪现一下之后在进行加载数据,用户体验十分不好 解决办法: <template> <el-table :data=&qu ...

  4. HC大会,华为联合合作伙伴发布一站式物联网IoT开发工具小熊派BearPi

    传统的物联网产品开发步骤复杂,涉及硬件开发.软件开发.云端开发等众多流程.而且产品的开发周期长.开发成本高.产品稳定性不佳.维护成本高.而物联网设备本身市场竞争激烈,价格低,设备更新迭代快,所以在保证 ...

  5. C#DateTime格式转换全介绍

    DateTime与字符串转换: DateTime()与转换为字符串主要依靠DateTime().ToString(string format) 函数,以我的理解,参数format大体分为单个字母和多个 ...

  6. 转:Java实现敏感词过滤

    敏感词.文字过滤是一个网站必不可少的功能,如何设计一个好的.高效的过滤算法是非常有必要的.前段时间我一个朋友(马上毕业,接触编程不久)要我帮他看一个文字过滤的东西,它说检索效率非常慢.我把它程序拿过来 ...

  7. python使用mysql的一些坑

    注意:如果你用的是python3.x,直接去看第四个问题 遇到的第一个问题 正常来说直接执行pip安装,就是可以的,但是MySQL-python偏偏比较独特 pip install MySQL-pyt ...

  8. 华为OSPF与ACL综合应用

    一. 实验拓扑图 二.实验要求 1.企业内网运行OSPF路由协议,区域规划如图所示:2.财务和研发所在的区域不受其他区域链路不稳定性影响:3.AR1.AR2.AR3只允许被IT登录管理:4.YF和CW ...

  9. 使用Fiddler 对ios 设备进行HTTPS 的抓取

    http://blog.csdn.net/skylin19840101/article/details/43485911  使用Fiddler 对ios 设备进行HTTPS 的抓取

  10. 关于maven-assembly-plugin插件打包,有部分无法打包的情况解决方法

    今天在使用maven-assembly-plugin 对生产者进行打包,然后在linux中发布时.将包打包之后,发现mybtis的xml无法识别,然后查看原因说是没有这个包,我当时就纳闷了,都是基操( ...