.NET进阶篇06-async异步、thread多线程2

知识须要不断积累、总结和沉淀,思考和写做是成长的催化剂web

内容目录

1、线程Thread
一、生命周期
二、后台线程
三、静态方法
1.线程本地存储
2.内存栅栏
四、返回值
2、线程池ThreadPool
一、工做队列
二、工做线程和IO线程
三、和Thread区别
四、定时器

1、线程Thread

.NET中线程操做封装为了Thread类,可让开发者对线程进行直观操做。Thread提供了实例方法用于管理线程的生命周期和静态方法用于控制线程的一些访问存储等一些外在的属性,至关于工做空间环境变量了网络

一、生命周期

线程的生命周期有建立、启动、可能挂起、等待、恢复、异常、而后结束。用Thread类能够容易控制一个线程的全生命周期多线程

Thread类的构造函数重载能够接受ThreadStart无参数和ParameterizedThreadStart有参数的委托,而后调用实例的Start()方法启动线程。Thread的构造函数的带有参数的委托,参数是一个object类型,由于咱们能够传入任何信息app

Thread t1 = new Thread(() => {Console.WriteLine($"新线程  {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
});
t1.Start();
Thread t2 = new Thread((obj) => {Console.WriteLine($"新线程  {Thread.CurrentThread.ManagedThreadId.ToString("00")},参数 {obj.ToString()}");
});
t2.Start("hello kitty");

线程启动后,能够调用线程的Suspend()挂起线程,线程就会处于休眠状态(不继续执行线程内代码),调用Resume()唤醒线程,还有一个不太建议使用的Abort()经过抛出异常的方式来销毁线程,随后线程的状态就会变为AbortRequested框架

经常使用的还有线程的等待,在主线程上启用工做线程后,有时须要等待工做线程的完成后,主线程才继续工做。能够调用实例方法Join(),固然咱们能够传入时间参数来代表我主线程最多等你多久异步

二、后台线程

上一章咱们知道Thread默认建立的是前台线程,前台线程会阻止系统进程的退出,就是启动以后必定要完成任务的后台线程会伴随着进程的退出而退出。经过设置属性IsBackground=true改成后台线程。另外还能够经过设置Priority指定线程的优先级。但这个并不总会如你所想设置了高优先级就必定最早执行。操做系统会优化调度,这也是线程不太好控制的缘由之一函数

三、静态方法

上面介绍的都是Tread的实例方法,Thread还有一些经常使用静态方法。有时线程设置不当,会有些意想不到的的bug性能

1.线程本地存储

AllocateDataSlot和AllocateNamedDataSlot用于给全部线程分配一个数据槽。像下面例子所示,若是不在子线程中给数据槽中放入数据,是获取不到其余线程往里面放的数据。测试

var slot= Thread.AllocateNamedDataSlot("testSlot");
//Thread.FreeNamedDataSlot("testSlot");
Thread.SetData(slot, "hello kitty");
Thread t1 = new Thread(() => {//Thread.SetData(slot, "hello kitty");var obj = Thread.GetData(slot);Console.WriteLine($"子线程:{obj}");//obj没有值
});
t1.Start();var obj2 = Thread.GetData(slot);
Console.WriteLine($"主线程:{obj2}");

在声明数据槽的时候.NET提醒咱们若是要更好的性能,请使用ThreadStaticAttribute标记字段。什么意思?咱们来看下面这个例子

//
// 摘要:
//     在全部线程上分配未命名的数据槽。 为了得到更好的性能,请改用以 System.ThreadStaticAttribute 特性标记的字段。
//
// 返回结果:
//     全部线程上已分配的命名数据槽。
public static LocalDataStoreSlot AllocateDataSlot();

例子中的若是不在静态字段上标记ThreadStatic输出结果就会一致。ThreadStatic标记指示各线程的静态字段值是否惟一

[ThreadStatic]
static string name = string.Empty;
public void Function()
{name = "kitty";Thread t1 = new Thread(() => {Console.WriteLine($"子线程:{name}");//输出空});t1.Start();Console.WriteLine($"主线程:{name}");//输出kitty
}

还有一个ThreadLocal提供线程数据的本地存储,用法和上面同样,在每一个线程中声明数据仅供本身使用

ThreadLocal<string> local = new ThreadLocal<string>() { };
local.Value = "hello kitty";
Thread t = new Thread(() => {Console.WriteLine($"子线程:{local.Value}");
});
t.Start();
Console.WriteLine($"主线程:{local.Value}");

上面的静态方法用于线程的本地存储TLS(Thread Local Storage),Thread.Sleep方法在开发调试时也是常常用的,让线程挂起指定的时间来模拟耗时操做

2.内存栅栏

先说一个常识问题,为何咱们发布版本时候要用Release发布?Release更小更快,作了不少优化,但优化对咱们是透明的(计算机里透明认为是个黑盒子,内部逻辑细节对咱们不开放,和生活中透明意味着彻底掌握了解不欺瞒恰好相反),通常优化不会影响程序的运行,咱们先借用网上的一个例子

bool isStop = false;
Thread t = new Thread(() => {bool isSuccess = false;while (!isStop){isSuccess = !isStop;}
});
t.Start();
Thread.Sleep(1000);
isStop = true;
t.Join();
Console.WriteLine("主线程执行结束");

上面例子若是在debug下能正确执行完直到输出“主程序执行结束”,然而在release下却一直会等待子线程的完成。这里子线程中isStop一直为false。首先这是一个由多线程共享变量引发的问题,因此咱们建议最好的解决办法就是尽可能不共享变量,其次可使用Thread.MemoryBarrier和VolatileRead/Write以及其余锁机制牺牲一点性能来换取数据的安全。(上面例子测试若是在子线程while中进行Console.writeLine操做,奇怪的发现release下也能正常输出了,猜想应该是进行了内存数据的更新)

release优化会将t线程中的isStop变量的值加载到CPU Cache中,而主线程修改了isStop值在内存中,因此子线程拿不到更新后的值,形成数据不一致。那么解决办法就是取值时从内存中获取。Thread.MemoryBarrier()就可让在此方法以前的内存写入都及时的从CPU Cache中更新到内存中,在此以后的内存读取都要从内存中获取,而不是CPU Cache。在例子中的while内增长Thread.MemoryBarrier()就能避免数据不一致问题。VolatileRead/Write是对MemoryBarrier的分开解释,从处理器读取,从处理器写入。

四、返回值

前面声明线程时,能够传递参数,那么想要有返回值该如何去作呢?Thread并无提供返回值的操做,后面.NET给出的对Thead的高级封装给出了解决方案,直接使用便可。那目前咱们使用thread类就要本身实现下带有返回值的线程操做,都是经过委托实现的,这里简单介绍一种,(共享外部变量也是能够,不建议)

private Func<T> ThreadWithReturn<T>(Func<T> func)
{T t = default(T);Thread thread = new Thread(() =>{t = func.Invoke();});thread.Start();return () =>{thread.Join();return t;};
}
//调用
Func<int> func = this.ThreadWithReturn<int>(() =>
{Thread.Sleep(2000);return DateTime.Now.Millisecond;
});
int iResult = func.Invoke();

2、线程池ThreadPool

.NET起初提供Thread线程类,功能很丰富,API也不少,因此使用起来比较困难,何况线程还不都是很像理想中运行,因此从2.0开始提供了ThreadPool线程池静态类,全是静态方法,隐藏了诸多Thread的接口,让线程使用起来更轻松。线程池可用于执行任务、发送工做项、处理异步 I/O、表明其余线程等待以及处理计时器

一、工做队列

经常使用ThreadPool线程池静态方法QueueUserWorkItem用于将方法排入线程池队列中执行,若是线程池中有闲置线程就会执行,QueueUserWorkItem方法的参数能够指定一个回调函数委托而且传入参数,像下面这样

ThreadPool.QueueUserWorkItem((obj) => {Console.WriteLine($"线程池中线程  {Thread.CurrentThread.ManagedThreadId.ToString("00")} ,传入 {obj.ToString()}");},"hello kitty");
二、工做线程和IO线程

通常异步任务的执行,不涉及到网络文件等IO操做的,计算密集型,开发者来调用。而IO线程通常用在文件网络上,是CLR调用的,开发者无需管。工做线程发起文件访问调用,由驱动器完成后通知IO线程,IO线程则执行异步任务的回调函数

获取和设置最小最大的工做线程和IO线程

ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
ThreadPool.SetMaxThreads(16, 16);
ThreadPool.SetMinThreads(8, 8);
三、和Thread区别

若是计算机只有8个核,同时能够有8个任务运行。如今咱们有10个任务须要运行,用Thread就须要建立10个线程,用ThreadPool可能只须要利用8个线程就行,节约了空间和时间。线程池中的线程默认先启动最小线程数量的线程,而后根据须要增减数量。线程池使用起来简单,但也有一些限制,线程池中的线程都是后台线程,不能设置优先级,经常使用于耗时较短的任务。线程池中线程也能够阻塞等待,利用ManualResetEvent去通知,但通常不会使用。

四、定时器

.NET中有不少能够实现定时器的功能,在ThreadPool中,咱们能够利用RegisterWaitForSingleObject来注册一个指定时间的委托等待。像下面这样,将每隔一秒就输出消息

ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(true), new WaitOrTimerCallback((obj, b) =>
{Console.WriteLine($"obj={obj},tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}");
}),"hello kitty",1000,false);

咱们日常见过比较多的仍是timer类,timer类在.net内是好几个地方都有的,在System.Threading、
System.Timer、System.Windows.Form、System.Web.UI等里面都有Timer,后面都是在第一个System.Threading里的Timer扩展

System.Threading.Timer timer = new System.Threading.Timer((obj) =>
{Console.WriteLine($"obj={obj},tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}");
},"hello kitty",1000,1000);

timer的底层有一个TimerQueue,利用ThreadPool.UnsafeQueueUserWorkItem来完成定时功能,和上面咱们使用的ThreadPool定时器有一点区别

实际开发中,简单定时timer就够用,但通常业务场景比较复杂,须要定制个性化的定时器,好比每个月几号执行,每个月第几个星期几,几点执行,工做日执行等。所以咱们使用Quarz.NET定时框架,后面框架整合时会用到,用起来也是很简单的

先就啰嗦这两点吧,下一篇应该是Task、Parallel以及Async/Await,而后总结介绍下C#的线程模式、线程同步锁机制、异常处理,线程取消,线程安全集合和常见的线程问题

天长水阔,见字如面,随缘更新,拜了个拜~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/588386.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二叉树的后序遍历,力扣

目录 建议先刷一下中序遍历 题目地址&#xff1a; 题目&#xff1a; 我们直接看题解吧&#xff1a; 解题方法&#xff1a; 注&#xff1a; 解题分析&#xff1a; 解题思路&#xff1a; 代码实现&#xff1a; 代码实现&#xff08;递归&#xff09;&#xff1a; 代码实现&#x…

LINUX 抓包工具Tcpdump离线安装教程

本次教程基于内网环境无法访问网络使用安装包进行安装抓包工具 1、首先给大家看下一共有6个安装包&#xff0c;依次进行解压&#xff0c;包我就放到csdn上了&#xff0c;需要的可以联系我进行下载 2打包然后传到服务器任意一个目录下&#xff0c;进入到当前目录&#xff0c;然后…

React快速入门之交互性

响应事件 创建事件处理函数 处理函数名常以handle事件名命名 function handlePlayClick() {alert(Playing);}传递事件处理函数 函数名、匿名两种方式&#xff01; function PlayButton() {function handlePlayClick() {alert(Playing);}return (<Button handleClick{handl…

VS2019+OpenCV4.7.0+OpenCV_contrib4.7.0+CUDA安装+配置视频硬解码保姆级别教程

在算法开发过程中&#xff0c;涉及基于opencv的rtsp流硬解码&#xff0c;这里设计结合当前所有的资料&#xff0c;实现了现有opengl相关的所有跟视频硬解码相关的功能&#xff0c;下面对opencv4.7.0的编译流程进行说明&#xff1a; 一、准备工作 下载opencv &#xff1a;open…

matplotlib绘制柱状图

代码 import matplotlib.pyplot as plt import numpy as np# 数据 categories [denoise, double-digit, 100% 5R] existence [0.9778, 0.9768, 0.9767] non_existence [0.9772, 0.9767, 0.9778]# 设置每组柱状图的宽度 bar_width 0.25# 计算每组柱状图的位置 x np.arange…

最优轨迹生成(一)—— 微分平坦

本系列文章是学习深蓝学院-移动机器人运动规划课程第五章最优轨迹生成 过程中所记录的笔记&#xff0c;本系列文章共包含四篇文章&#xff0c;依次介绍了微分平坦特性、无约束BVP轨迹优化、无约束BIVP轨迹优、 带约束轨迹优化等内容 本系列文章链接如下&#xff1a; 最优轨迹生…

五、Spring AOP面向切面编程

本章概要 场景设定和问题复现解决技术代理模式面向切面编程思维&#xff08;AOP&#xff09;Spring AOP框架介绍和关系梳理 5.1 场景设定和问题复现 准备AOP项目 项目名&#xff1a;spring-aop-annotation pom.xml <dependencies><!--spring context依赖--><…

一文讲清数据资产化之确权和估值

《中共中央 国务院关于构建数据基础制度更好发挥数据要素作用的意见》已发布一年&#xff0c;数据资产化和入表已成为2023年的热门话题&#xff0c;随着2023年底国家数据局吹风《"数据要素x"三年行动计划&#xff08;2024-2026年&#xff09;》即将发布&#xff0c;这…

Android 13 - Media框架(27)- ACodec(五)

前面几节我们了解了OMXNodeInstance是如何处理setPortMode、allocateBuffer、useBuffer的&#xff0c;这一节我们再回到ACodec&#xff0c;来看看 ACodec start 的其他部分。 我们首先来回顾一下&#xff0c;ACodec start 的状态切换以及处理的事务&#xff0c;我们用一张不太准…

EOS运行启动 keosd 和 nodeos

EOS运行启动 keosd 和 nodeos 启动keosd:启动nodeos 安装完eosio后keosd和nodeos是一起带着的&#xff0c;我把EOS的运行keosd 和 nodeos官网教程放上来&#xff1a; https://developers.eos.io/welcome/latest/getting-started-guide/local-development-environment/start-nod…

软件工程期末复习习题

知识点总结 第一章&#xff1a;软件工程概述 1、软件的定义&#xff1a;在运行中能提供所希望的功能与性能的程序使程序能够正确运行的数据及其结构描述软件研制过程和方法所用的文档。 2、软件危机&#xff1a;软件开发的生产率远远不能满足客观需要。开发的软件产品往往不能…

【PowerMockito:编写单元测试过程中采用when打桩失效的问题】

问题描述 正如上图所示&#xff0c;采用when打桩了&#xff0c;但是&#xff0c;实际执行的时候还是返回null。 解决方案 打桩时直接用any() 但是这样可能出现一个mybatisplus的异常&#xff0c;所以在测试类中需要加入以下代码片段&#xff1a; Beforepublic void setUp() …

你真的懂Hello World!吗?(编译与链接,静态链接与动态链接)

&#x1f4ab;Hello World! 对于大家来说Hello World!应该是最熟悉不过的一句话&#xff0c;我们从Hello World!走进了计算机的世界&#xff0c;但是你真的了解Hello World!吗&#xff1f;你又思考过它背后蕴含的机理吗&#xff1f;他是怎么从代码变成程序的你真的思考过吗&…

Spring Boot程序输出远程访问IP

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringBoot开发》。&#x1f3af;&#x1f3af;…

03 HAL库下UART的使用

引言&#xff1a; 需要使用到的uart调试工具在文章最后的资料里面 题外话&#xff1a;uart和usart的区别 UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;和USART&#xff08;Universal Synchronous/Asynchronous Receiver/Transmitter&#xff09;…

获取Windows10系统原始安装日期

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 目标 获取Windows10系统最原始的安装日期&#xff1b;例如&#xff1a;刚买电脑时安装系统的时间。 步骤 第一步&#xff0c;请打开PowerShell&#xff0c;单击Windows P…

评价类问题:层次分析法

引言&#xff1a;打分法 一、解决评价类问题的三个方面 二、如何寻找评价准则 三、如何确定权重 &#xff08;1&#xff09;分而治之的思想 &#xff08;2&#xff09; 层次分析法思想&#xff1a; 指标重要性&#xff08;满意程度&#xff09; 判断矩阵&#xff08;正反矩…

分布式【4. 什么是 CAP?】

什么是 CAP&#xff1f; C 代表 Consistency&#xff0c;一致性&#xff0c;是指所有节点在同一时刻的数据是相同的&#xff0c;即更新操作执行结束并响应用户完成后&#xff0c;所有节点存储的数据会保持相同。 A 代表 Availability&#xff0c;可用性&#xff0c;是指系统提…

数据结构与算法笔记

数据结构&#xff1a; 就是指一组数据的存储结构 算法&#xff1a; 就是操作数据的一组方法 数据结构和算法 两者关系 数据结构和算法是相辅相成的。数据结构是为算法服务的&#xff0c;算法要作用在特定的数据结构之上。 数据结构是静态的&#xff0c;它只是组织数据的一…

Kubernetes网络-VXLAN

一. 网络基础 1. 计算机网络的分层 如今连接方式也越来也丰富&#xff0c;网线、WiFi、蓝牙、光纤&#xff0c;甚至我们普通的电线、照明所用的灯光&#xff0c;都可以作为接入网络的介质。如此庞大的网络&#xff0c;丰富多样的设备&#xff0c;计算机网络技术能把它们统一起…