初探.net framework 下的异步多线程

目录

1、多线程的出现条件

2、Thread和ThreadPool的相关Api及用法

3、Task和Parallel的相关Api及用法

4、Async&&Await

多线程的出现条件

  • 用户在执行一个操作的时候,可以同时的执行一些其他操作。(例如在写入一个文件的时候,可以同时推送一条信息;还有一种情况,就是例如在编写Winform代码时候,提交一个比较费时的操作,这时候会造成UI界面假死,此时就可以把这个费事的操作交给一个子线程来完成,亦或者方法的异步调用。)
  • 我们的CPU是高速的,分时间片执行的,操作系统将其封装为一个个的线程,多个线程运行于一个进程之中,在我们的.net framework框架,将操作系统级别的线程 做了再次封装,就是我们所了解到的Thread类。
  • 这里我们再谈下多线程异步的区别。在我们编写C#代码的时候,异步方法是这么调用的。
    在启用BeginInvoke方法时候,action异步调用。此时可以看到,回掉函数由一个Id为3的线程来执行的。在.net framework中,异步就是由主线程开启一个子线程来完成回调任务。
            Console.WriteLine("***************委托的异步调用***************");
Console.WriteLine($"this is main_{Thread.CurrentThread.ManagedThreadId}");
Action<string> act = t => { Console.WriteLine(t); }; //声明一个委托,接受一个string类型的参数。这里打印参数"ssss"
IAsyncResult result = null;
AsyncCallback asyncCallback = t => //声明一个AsyncCallback 也就是BeginInvoke的第二个参数,代表一个异步回掉函数
{
Console.WriteLine($"{string.ReferenceEquals(t, result)}"); //AsyncCallback 是一个委托,IAsyncResult参数就是BeginInvoke的返回值
Console.WriteLine($"this is asynccallback{Thread.CurrentThread.ManagedThreadId}");
};
result = act.BeginInvoke("ssss", asyncCallback, null);
act.EndInvoke(result);
//result.AsyncWaitHandle.WaitOne();
Console.Read();

Thread和ThreadPool的相关Api及用法

  • Thread 是.net framework 1.X版本的类(没记错的话),Thread类接受一个ThreadStart的委托,这个委托没有参数,没有返回值。我们这里定义一个耗时的测试方法(下文中都用这个方法代表一些费事的逻辑操作。)写一个测试方法,创建五个线程分别执行这个方法。
        private static void DoSomethingLong(string name)
{
Console.WriteLine($"*************DoSomethingLong Start {Thread.CurrentThread.ManagedThreadId}*************");
long Result = 0;
for(int i = 0; i < 1000000000; i++)
{
Result += i;
}
Console.WriteLine("*************DoSomethingLong Start {Thread.CurrentThread.ManagedThreadId}*************");
}
private void ThreadTest()
{
for(int i = 0; i < 5; i++)
{
Thread thread = new Thread(() => { DoSomethingLong(""); });
thread.Start();
}
}
  • 使用Thread来编写一个带有回调的方法
        private void ThreadCallBackTest()                      //这执行下
{
Thread thread = new Thread(
() => ThreadCallBack(() => { Console.WriteLine("这是Thread接收的threadStart"); },
() => Console.WriteLine("这是回调函数")));
thread.Start();
}
private void ThreadCallBack(ThreadStart threadStart,Action act) //这里包装一层,Thread的回调方法放入Action参数中
{
ThreadStart start = new ThreadStart(() => //ThreadStart 本身是一个无参数无返回值的委托, 将ThreadStart和Action 都执行下
{
threadStart.Invoke(); //这里相当于把原来的ThreadStart(()=>{} )包了一层,里面又一个ThreadStart(()=>{} ),需要体会下
act.Invoke();
});
Thread thread = new Thread(start); //启用一个线程执行。
thread.Start();
}
  • 使用Thread写一个带返回值的委托。
       private void ThreadReturnParTest()
{
Thread thread = new Thread(() =>
{
var e = ThreadReturnPar<string>(() => { return "ssss"; }); //定义ThreadReturnPar的参数,返回一个ssss
var s = e.Invoke(); //这里等待,thread.Join()
Console.WriteLine(s);
});
thread.Start();
}
private Func<T> ThreadReturnPar<T>(Func<T> func) //核心方法,这里接受一个Func<T>
{
T t = default(T); //声明一个T
ThreadStart start = new ThreadStart(() => //启用ThreadStart,给这里的t附上返回值。这里还没有执行,回调的时候才会执行。
{
t = func.Invoke();
});
Thread thread = new Thread(start); //开启一个新的线程,执行这个ThreadStart
thread.Start();
return () =>
{
while (thread.ThreadState != System.Threading.ThreadState.Stopped) //判断执行执行这个ThreadStart的委托是否已经执行完,
{ //返回一个委托,哪里需要接受这个返回值,哪里等待thread.Join();
thread.Join();
}
return t;
};
}
  • ThreadPool是.net framework 2.X版本的类(没记错的话),线程池线程做一个池化的管理(对应设计模式为享元模式),使用ThreadPool时候不再由.netframework 框架从操作系统层面创建一个新的线程,而是由ThreadPool统一管理,我们向ThreadPool申请一个线程,使用完了以后把这个线程资源归还给ThreadPool
  • ThreadPool的QueueUserWorkItem方法接收一个WaitCallback委托,这个委托的参数就是QueueUserWorkItem的第二个参数。这里还要介绍一个对象,ManualResetEvent对象,可以把这个对象理解为一把锁,这个锁有个初始状态,ManualResetEvent对象的Reset方法阻塞线程,Set方法使阻塞线程继续运行。
        private void ThreadPoolTest()
{
Console.WriteLine($"ThreadPoolTest start {DateTime.Now.Millisecond}"); /*第一句打印*/ /*add ManualResetEvent 第一句打印*/
ManualResetEvent mre = new ManualResetEvent(true); //实例化一个初始状态、理解为一把锁
mre.Reset();
ThreadPool.QueueUserWorkItem(
new WaitCallback(
t => {
Console.WriteLine($"this is t {t}"); /*第三句打印*/ /*add ManualResetEvent 第二句打印*/
Thread.Sleep(1000);
Console.WriteLine("this is threadpool queue waitcallback"); /*第四句打印*/ /*add ManualResetEvent 第三句打印*/
mre.Set(); //ManualResetEvent关闭
}), "lmc");
mre.WaitOne(); //等待ManualResetEvent打开才执行后面的代码
Console.WriteLine($"ThreadPoolTest End {DateTime.Now.Millisecond}"); /*第二句打印*/ /*add ManualResetEvent 第四句打印*/
}

Task和Parallel的相关Api及用法

  • Task类基于.net framework3.5, Task基于线程池,Task创建方式由两种,Task 可以基于工厂创建;也可以用new Task创建 ;相关Api WaitAll 等待所有Task全部完成了再执行; WaitAny 等待一个Task执行完了之后再执行后面的; (以上两个API会卡主线程); Task.Factory.ContinueWhenAll 以回调形式等待所有任务完成后执行一个委托。Task.Factory.ContinueWhenAny以回调形式等待任意一个任务完成后执行一个委托。四个Task的等待执行的Api需要记好。
        private void TaskTest()
{
List<Task> tasklist = new List<Task>();
Console.WriteLine($"TaskTest start_{Thread.CurrentThread.ManagedThreadId} {DateTime.Now.Millisecond}");
for (int i = 0; i < 5; i++)
{
string name = $"lmc_{i}";
Task task = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine($"{name}_{Thread.CurrentThread.ManagedThreadId} " );
});
tasklist.Add(task);
}
Console.WriteLine("before waitall");
Task.WaitAll(tasklist.ToArray());//线程等待全部
Console.WriteLine("after waitall");
Console.WriteLine("before waitany");
Task.WaitAny(tasklist.ToArray());//线程等待某一个
Console.WriteLine("after waitany");
Task.Factory.ContinueWhenAll(tasklist.ToArray(),
tlist => { Console.WriteLine($"ContinueWhenAll_{tlist.Count()}_{Thread.CurrentThread.ManagedThreadId}"); //回调形式等待所有任务完成后打印一句ContinueWhenAll_
Console.WriteLine($"ContinueWhenAll_{Thread.CurrentThread.ManagedThreadId}"); });
Task.Factory.ContinueWhenAny(tasklist.ToArray(),
t => { Console.WriteLine($"ContinueWhenAny_{t.Id}_{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine($"ContinueWhenAny_{Thread.CurrentThread.ManagedThreadId}"); }); //回调形式等待任意一个任务完成后打印一句ContinueWhenAny
Console.WriteLine($"TaskTest End_{Thread.CurrentThread.ManagedThreadId} {DateTime.Now.Millisecond}");
}
  • Parallel并行任务,主线程CPU参与计算,Invoke方法接受一个委托的数组。Parallel的另外两个常用的方法是ForForeach,两个方法类似,这里拿For来举个例子。(仔细观察下就像是Task.WaitAll)
        private void ParallelTest()
{
Console.WriteLine($"ParallelTest start_{Thread.CurrentThread.ManagedThreadId} {DateTime.Now.Millisecond}");
//Parallel
Parallel.Invoke(
() => { Console.WriteLine($"ParallelTest_1_{Thread.CurrentThread.ManagedThreadId}"); },
() => { Console.WriteLine($"ParallelTest_2_{Thread.CurrentThread.ManagedThreadId}"); },
() => { Console.WriteLine($"ParallelTest_3_{Thread.CurrentThread.ManagedThreadId}"); },
() => { Console.WriteLine($"ParallelTest_4_{Thread.CurrentThread.ManagedThreadId}"); }
);
ParallelOptions parallelOptions = new ParallelOptions(); //创建一个parallelOptions对象
parallelOptions.MaxDegreeOfParallelism = 3; //设置最大并发任务数量
Parallel.For(1, 10, parallelOptions,(t,state) => //ParallelLoopState参数 使并行循环迭代与其他迭代交互退出当前线程。
{
Thread.Sleep(1000);
Console.WriteLine($"ParallelTest_{t}_{Thread.CurrentThread.ManagedThreadId}_{DateTime.Now.Millisecond}");
state.Stop();
return;
});
Console.WriteLine($"ParallelTest end_{Thread.CurrentThread.ManagedThreadId} {DateTime.Now.Millisecond}");
}

Async&&Await

  • 在.netframework 4.5 出来以后,我们经常能看到,async和await两个关键字,代表方法的异步执行。使用await关键字必须要在async关键字修饰的方法下。主线程遇到await关键字就立即返回,把剩下的任务交由一个子线程来回调完成。这个过程就像是Task.Factory.ContinueWhenAny,参数就是await 后面的代码组成的一个委托。
        static void Main(string[] args)
{
Program prm = new Program();
Console.WriteLine("1"); //第一句执行
prm.AsyncTest(); //进入异步方法
Console.WriteLine("2"); //第三句执行
}
private async Task AsyncTest()
{
Console.WriteLine($"this is async task Main Start_{Thread.CurrentThread.ManagedThreadId}_{DateTime.Now.Millisecond}"); //第二句执行
await Task.Factory.StartNew(() => //这里由于遇到了await关键字,所以主线程返回
{
Thread.Sleep(3000);
Console.WriteLine($"this is async task Son Start__{Thread.CurrentThread.ManagedThreadId}_{DateTime.Now.Millisecond}"); //第四句执行
Console.WriteLine($"this is async task Son End_{Thread.CurrentThread.ManagedThreadId}_{DateTime.Now.Millisecond}"); //第五句执行
});
//遇到await关键字主线程返回,剩下的任务由子线程回调完成。
Console.WriteLine($"this is async task Main End_{Thread.CurrentThread.ManagedThreadId}_{DateTime.Now.Millisecond}"); //第六句执行
}
  • 最后让我们来看下处理多线程的异常。多线程运行中,子线程执行的任务抛出的异常不会主动影响主线程是其停止,最好的方法就是使用CancellationTokenSource对象。一个线程执行时候,首先判断下CancellationTokenSource对象的标识、假如异常,取消执行。一个线程的终止或异常,由它自身来完成。
        private void TaskInteractive()
{
try
{
CancellationTokenSource cts = new CancellationTokenSource();
TaskFactory taskFactory = new TaskFactory();
List<Task> tasklist = new List<Task>();
for (int i = 0; i < 40; i++)
{
string name = $"this is {i}";
Action<object> act = t =>
{
try
{
Thread.Sleep(500);
if (!cts.IsCancellationRequested) //判断标识是否取消
{
if (t.ToString().Equals("this is 11")) //当执行到第11个任务时候,修改标识cts.token,取消后面的任务执行。
{
cts.Cancel();
throw new Exception($"{t}执行失败");
}
if (t.ToString().Equals("this is 12"))
{
cts.Cancel();
throw new Exception($"{t}执行失败");
}
Console.WriteLine($"{t}执行成功");
}
else
{
Console.WriteLine($"{t}********执行放弃********");
cts.Token.ThrowIfCancellationRequested(); //抛出一个AggregateException异常
}
}
catch (Exception ex)
{
Console.WriteLine($"this is logging {ex.Message}");
}
};
tasklist.Add(taskFactory.StartNew(act, name,cts.Token));
}
Task.WaitAll(tasklist.ToArray());
}
catch (AggregateException aex)
{
foreach (var ex in aex.InnerExceptions)
{
Console.WriteLine(ex.Message);
}
}
}

初探.net framework 下的异步多线程的更多相关文章

  1. c#中@标志的作用 C#通过序列化实现深表复制 细说并发编程-TPL 大数据量下DataTable To List效率对比 【转载】C#工具类:实现文件操作File的工具类 异步多线程 Async .net 多线程 Thread ThreadPool Task .Net 反射学习

    c#中@标志的作用   参考微软官方文档-特殊字符@,地址 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/toke ...

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

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

  3. 异步多线程 Async

    进程:进程是一个程序在电脑运行时,全部资源的合集叫进程 线程:是程序的最小执行单位,包含计算资源,任何一个操作的响应都是线程完成的.   多线程:多个线程并发执行   Thread 是.net框架封装 ...

  4. NET 异步多线程,THREAD,THREADPOOL,TASK,PARALLEL

    .NET 异步多线程,THREAD,THREADPOOL,TASK,PARALLEL,异常处理,线程取消 今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主 ...

  5. Task/Parallel实现异步多线程

    代码: #region Task 异步多线程,Task是基于ThreadPool实现的 { //TestClass testClass = new TestClass(); //Action<o ...

  6. PHP 命令行模式实战之cli+mysql 模拟队列批量发送邮件(在Linux环境下PHP 异步执行脚本发送事件通知消息实际案例)

    源码地址:https://github.com/Tinywan/PHP_Experience 测试环境配置: 环境:Windows 7系统 .PHP7.0.Apache服务器 PHP框架:ThinkP ...

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

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

  8. c#异步多线程

    1.asyncrel = delegate.BeginInvoke实现委托异步调用. 2.异步等待 asyncrel.IsCompleted用于判断是否执行完毕 or EndInvoke用于等待执行完 ...

  9. PHP下的异步尝试二:初识协程

    PHP下的异步尝试系列 如果你还不太了解PHP下的生成器,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunkify ...

随机推荐

  1. uni-app的专属强大自适应单位upx,但是这个这是一个大坑,不能动态赋值解决办法

    uni-app 使用 upx 作为默认尺寸单位, upx 是相对于基准宽度的单位,可以根据屏幕宽度进行自适应.uni-app 规定屏幕基准宽度750upx. 开发者可以通过设计稿基准宽度计算页面元素 ...

  2. vue货币格式化组件、局部过滤功能以及全局过滤功能

    一.在这里介绍一个vue的时间格式化插件: moment 使用方法: .npm install moment --save. 2 定义时间格式化全局过滤器 在main.js中 导入组件 import ...

  3. 小程序viewflex布局的对齐不对的问题

    index.wxml: <view class="container"> <view class="nav-container"> &l ...

  4. 基于分布式框架 Jepsen 的 X-Cluster 正确性测试

    转自:https://mp.weixin.qq.com/s/iOe1VjG1CrHalr_I1PKdKw 原创 2017-08-27 严祥光(祥光) 阿里巴巴数据库技术 1 概述 AliSQL X-C ...

  5. SSL/TLS 加密新纪元 - Let's Encrypt

    转自: https://linux.cn/article-6565-1.html SSL/TLS 加密新纪元 - Let's Encrypt 根据 Let's Encrypt 官方博客消息,Let's ...

  6. 一步一步实现基于GPU的pathtracer(二):求交算法

    不管是哪种全局光照算法,最根本的都要落实到光线与物体的求交.主要分为光线与参数曲面和非参数曲面的求交,典型的参数曲面有球.盒.圆柱等基本体及基本体的组合体,以及一些更为复杂的参数曲面.非参数曲面就是所 ...

  7. tree:以树形结构显示目录下的内容

    tree命令 1.命令详解 [功能说明] tree命令的中文意思为“树”,功能是以树形结构列出指定目录下的所有内容包括所有文件.子目录及子目录里的目录和文件. [语法格式] tree [option] ...

  8. 5.win上安装ES

    安装步骤如下: 1.安装JDK 至少1.8.0_73以上版本,使用 java -version 这个命令进行查看java的版本 2.下载和解压缩Elasticsearch安装包, 解压后目录结构: 3 ...

  9. Bootstrap关于表单控件(Radio,CheckBox)

    表单控件(复选框checkbox和单选择按钮radio) Bootstrap框架中checkbox和radio有点特殊,Bootstrap针对他们做了一些特殊化处理,主要是checkbox和radio ...

  10. luogu P4512 多项式除法 (模板题、FFT、多项式求逆)

    手动博客搬家: 本文发表于20181206 14:42:53, 原地址https://blog.csdn.net/suncongbo/article/details/84853342 题目链接: ht ...