.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.mfbz.cn/a/281643.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023年终总结丨很苦,很酷!

文章目录 个人简介丨了解博主写在前面丨博主介绍年终总结丨博主成就年终总结丨博主想说年终总结丨学习芝士年终总结丨未来展望写在后面丨新年快乐 个人简介丨了解博主 主页地址&#xff1a;https://blog.csdn.net/m0_68111267 荣誉身份 ⭐2022年度CSDN 社区之星 Top6 ⭐2023年…

app自动化测试(Android)–触屏操作自动化

导入TouchAction Python 版本 from appium.webdriver.common.touch_action import TouchActionJava 版本 import io.appium.java_client.TouchAction;常用的手势操作 press 按下 TouchAction 提供的常用的手势操作有如下操作&#xff1a; press 按下 release 释放 move…

web网站的工作流程和开发模式

web网站的工作流程和开发模式 基于Java Script封装的高级技术&#xff1a;Vue、Element、Nginx(前端程序部署的服务器) 初识Web前端 Web标准

学习动态规划解决不同路径、最小路径和、打家劫舍、打家劫舍iii

学习动态规划|不同路径、最小路径和、打家劫舍、打家劫舍iii 62 不同路径 动态规划&#xff0c;dp[i][j]表示从左上角到(i,j)的路径数量dp[i][j] dp[i-1][j] dp[i][j-1] import java.util.Arrays;/*** 路径数量* 动态规划&#xff0c;dp[i][j]表示从左上角到(i,j)的路径数量…

MySQL基础入门(一)

图片来自b站黑马程序员 数据库操作 DDL 查询&#xff1a; 1.查询所有的数据库 show databases; 2.查询当前的鹅数据库 select database; 创建 create database [if not exists] 数据库名 [default charset 字符集][collate 排序规则]; 删除 drop database [if exists] 数…

Transformer基本结构

Transformer基本结构 输入部分、编码部分、解码部分、输出部分 1、输入部分 原文本嵌入层及其位置编码器目标文本嵌入层及其位置编码器 位置编码器(PositionalEncoding)&#xff1a;将词汇位置不同可能会产生不同语义的信息加入到词张量中&#xff0c;以弥补位置信息的缺失 …

nodejs文心一言API接入

需求 在nodejs里面接入文心一言API&#xff0c;官方调用步骤API介绍 - 千帆大模型平台 | 百度智能云文档 大致流程 创建应用——>API授权——>获取访问凭证——>调用接口 创建应用 注册账号创建应用 首先注册百度云智能账号&#xff0c;登录进入百度智能云千帆控…

12.30_黑马数据结构与算法笔记Java

目录 320 全排列无重复 Leetcode47 321 组合 Leetcode77 分析 322 组合 Leetcode77 实现 323 组合 Leetcode77 剪枝 324 组合之和 Leetcode 39 325 组合之和 Leetcode 40 326 组合之和 Leetcode 216 327 N皇后 Leetcode51-1 328 N皇后 Leetcode51-2 329 解数独 Leetco…

STM32入门教程-2023版【3-2】使用库函数点亮GPIO灯

关注 点赞 不错过精彩内容 大家好&#xff0c;我是硬核王同学&#xff0c;最近在做免费的嵌入式知识分享&#xff0c;帮助对嵌入式感兴趣的同学学习嵌入式、做项目、找工作! 二、正式点亮一个LED灯 &#xff08;3&#xff09;使用库函数点亮GPIO灯 RCC初始化 首先用的是&…

python2.x编码Unicode字符串

1 python2.x编码Unicode字符串 python2.x默认编码方法为ASCII码。字符串赋值时按系统默认编码自动编码&#xff0c;通过decode()方法解码为Unicode&#xff0c;再通过encode()方法编码为指定码。 1.1 编码解码基础知识 1.1.1 位 位(bit)是计算机存储数据的最小单位&#xf…

基于PHP的高校学生宿舍信息系统

有需要请加文章底部Q哦 可远程调试 基于PHP的高校学生宿舍系统 一 介绍 此学生宿舍信息系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端jquery.js。系统角色分为学生&#xff0c;辅导员和宿管员。(附带配套设计文档) 技术栈&#xff1a;phpmysqljquery.jsphpstu…

段永平浙江大学捐赠;合计超10亿元;OpenAI 年收超16亿美元;邻汇吧5000万元C+轮融资

投融资 • 「邻汇吧」完成5000万元C轮融资&#xff0c;安吉政府产业基金投资• 投资者预计明年黄金价格或将创新高• 至臻云完成 A 轮数千万元融资 大模型 • ChatGPT 产品增长强劲 OpenAI 年化收入超 16 亿美元• 周鸿祎&#xff1a;明年大模型一方面追求“大”&#xff0c…

超真实随身WiFi测评,你确定不看一下?随身WiFi靠谱吗? 看完这篇文章你就懂了?随身WiFi真实评测

用了一年多的格行随身wifi&#xff0c;屏幕都磨花了。直接看图&#xff0c;都是自己实测&#xff01; 设备是去年买的&#xff0c;到现在也快1年了&#xff0c;一直有朋友蹲后续&#xff0c;现在把后续给大家&#xff01;到底是大牌子&#xff0c;确定是不跑路的随身wifi&…

解决基于VectorGrid的矢量瓦片Y轴偏移的问题

目录 前言 一、GeoServer的瓦片 1、GeoWebcache缓存配置 2、矢量瓦片本地缓存 3、瓦片访问 二、VectorGrid加载本地瓦片 1、加载关键代码 2、默认模式的问题 3、问题分析 4、tms参数修改 总结 前言 在前面的博文介绍中&#xff0c;在线连接如下&#xff1a;浅谈前端自定义…

国图公考:研究生可以考选调生吗?

研究生可以报考选调生吗?当然是可以的&#xff0c;但是同样需要满足一定的条件才可以。 除本科生外&#xff0c;具有硕士、博士学位的考生均可申请考试。但是&#xff0c;除了满足应届毕业生的身份&#xff0c;还需要满足年龄限制。一般来说&#xff0c;本科生不超过25岁&…

【损失函数】SmoothL1Loss 平滑L1损失函数

1、介绍 torch.nn.SmoothL1Loss 是 PyTorch 中的一个损失函数&#xff0c;通常用于回归问题。它是 L1 损失和 L2 损失的结合&#xff0c;旨在减少对异常值的敏感性。 loss_function nn.SmoothL1Loss(reductionmean, beta1.0) 2、参数 size_average (已弃用): 以前用于确定是…

CANopen DS402 Home offset理解

本文通俗解释CANopen DS402中Home offset的含义。 一 原本解释 CANopen DS402中规定对象字典项0x607C用于存放Home offset&#xff0c;文档中对其解释如下&#xff0c; The home offset object is the difference between the zero position for the application and the mach…

一起玩儿物联网人工智能小车(ESP32)——21. ESP32的LED PWM控制器说明

摘要&#xff1a;本文介绍ESP32的LED PWM控制器的详细原理说明 ESP32芯片提供了2种用于输出PWM的信号的PWM控制器&#xff0c;一种LED控制器&#xff08;LEDC&#xff09;&#xff0c;一种是马达PWM控制器&#xff08;MCPWM&#xff09;。其中MCPWM是专门用于马达调速控制的控制…

缺失的第一个正数(LeetCode 41)

文章目录 1.问题描述2.难度等级3.热门指数4.解题思路4.1 暴力4.2 排序4.3 哈希表4.4 空间复杂度为 O(1) 的哈希表4.5 置换 参考文献 1.问题描述 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级…

k8s搭建(详细演示完整一篇)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…
最新文章