8. C#多线程基础概念

文章目录

      • 一. 目标
      • 二. 技能介绍
        • ① 进程和线程
        • ② 为什么需要多线程
        • ③ C#实现多线程的方式
        • ④ 线程的操作(创建_终止_挂起_恢复)

一. 目标

  1. 进程和线程基本概念
  2. 为什么需要多线程?
  3. C#实现多线程的方式?
  4. 线程Thread的创建,终止,挂起和恢复?

二. 技能介绍

① 进程和线程
  • 什么是进程(Process)
  1. 一个正在运行的程序实例就是一个进程,拥有独立的内存空间和资源.
  2. 每个进程都在自己的内存空间内运行,相互之间不直接共享内存,进程间通信一般需要一些机制,比如进程间通信IPC.
  3. 每一个进程都有自己的一个主线程,而这个主线程是程序的入口点,它可以创建其他线程来执行不同的任务
  • 什么是线程(Thread)
  1. 线程是进程内的一个执行单元,也是操作系统可执行的最小单元.
  2. 一个进程中的多个线程共享进程的资源,它们之间可以共享数据.
  3. 线程在程序中是可以并行执行的.
  • 多进程

多进程指的是多个独立运行的进程,每个进程都有自己的内存控件,独立执行任务,相互之间不会收到影响.
多进程可以提高系统的并行性和稳定性,但是进程间的通信开销比较大

  • 多线程

多线程是指在同一个进程内同时执行多个线程.线程之间可以更方便地共享数据和通信,适用于需要高度协作和共享资源的任务.多线程可以提高程序的响应速度和资源利用率

② 为什么需要多线程

在软件开发中,我们可能会遇到下面这些需求:

  • 1. 图像用户界面GUI应用程序

主线程UI线程需要保持响应性,所以在执行耗时操作的的时候,要创建新的线程去操作,如果用主线程去执行耗时任务,界面将会出现卡顿,就影响了用户使用体验.

  • 2. 网络编程

在网络编程中,常常需要同时处理多个网络请求或者连接.使用多线程可以让程序更高效处理这些请求,避免阻塞主线程

  • 3. 并行计算

对于需要大量计算的任务,如数据处理,图像处理等,通过使用多线程可以充分利用多核处理器,加快任务完成速度

总结

  1. 提高效率
  2. 提高响应速度
  3. 充分利用多喝处理器
③ C#实现多线程的方式
  • 使用Thread类

语法

Thread thread = new Thread(()={});

例子

#region 1. 使用Thread创建线程Thread thread = new Thread(() =>
{Console.WriteLine("我是线程1,我采用的是Thread(lambda=>{}) 匿名表达式的方式");
});
thread.Start();
// thread线程启动之后,会继续往下执行,不会阻塞线程#endregion
  • 使用Task类

语法

Task task = Task.Run(()=> {})

例子

Task task = Task.Run(() =>
{Console.WriteLine("我是线程2,我采用的是Task(lambda=>{}) 匿名表达式的方式");
});
// Task.Wait()方法用于等待任务的完成,在调用该方法之后,当前现场会被阻塞,直到任务执行完成为止
task.Wait();
  • 使用ThreadPool类

语法

ThreadPool.QueueUserWorkItem((state)=>{})

例子

ThreadPool.QueueUserWorkItem((state) =>
{Console.WriteLine("我是线程3,我采用的是ThreadPool方式");
});
// 这种方式创建的线程是线程池创建线程,是不会阻塞主线程的,主线程会继续往下执行.
  • 使用Async/Await异步编程

例子

#region 4. 使用Async/Awaitawait Task.Run(() =>
{Console.WriteLine("我是线程4,我采用的额Async/Await的方式");
});#endregion
  • 使用Parallel.For方法

用于并行执行一个for循环,可以在多个线程中同时处理循环中的元素,可以在单独的线程上执行for循环中的数据和后面的计算表达式

例子:

Parallel.For(0, 10, i =>
{Console.WriteLine($"当前数据i = {i},线程Id = {Thread.CurrentThread.ManagedThreadId}");Thread.Sleep(1000);
});
  • 使用Parallel.ForEach方法

Parallel.ForEach方法用于遍历一个集合,在多个线程中同时处理集合中的元素

例子

var numbers = Enumerable.Range(0, 10);
Parallel.ForEach(numbers, num =>
{Console.WriteLine($"处理的数据 = {num},处理线程 = {Thread.CurrentThread.ManagedThreadId}");Thread.Sleep(200);
});
  • Parallel.Invoke 方法

Parallel.Invoke 方法用于并行执行多个操作,可以在多个线程中同时执行这些操作.什么意思呢,就是可以在Parallel.Invoke方法中传递多个方法(或者是匿名方法)作为参数,然后去并行执行这些方法

#region 7. Parallel.InvokeParallel.Invoke(() => { Console.WriteLine($"函数1,线程Id = {Thread.CurrentThread.ManagedThreadId}"); },() => { Console.WriteLine($"函数2,线程Id = {Thread.CurrentThread.ManagedThreadId}"); },() => { Console.WriteLine($"函数3,线程Id = {Thread.CurrentThread.ManagedThreadId}"); },() => { Console.WriteLine($"函数4,线程Id = {Thread.CurrentThread.ManagedThreadId}"); },() => { Console.WriteLine($"函数5,线程Id = {Thread.CurrentThread.ManagedThreadId}"); },() => { Console.WriteLine($"函数6,线程Id = {Thread.CurrentThread.ManagedThreadId}"); });#endregion
  • PLINQ的AsParallel方法

AsParallel方法用于将LINQ查询转换为并行查询,实现并行处理查询结果

例子:

#region 8. AsParallel 方法用于将LINQ查询转换为并行查询,实现并行处理查询结果var numbers02 = Enumerable.Range(10, 20);
var result = numbers.AsParallel().Where(num =>
{Console.WriteLine($"数据num: {num},所在线程ID: {Thread.CurrentThread.ManagedThreadId}");return num % 2 == 0;
}).ToList();#endregion
  • PLINQ的AsSequential 和 AsOrdered 方法

AsSequential 方法用于将并行查询转换为顺序查询,以保留查询结果的顺序性.
AsOrdered 方法用不指定查询结果的顺序行,确保结果按照源数据的顺序返回.

在一开始接触这两个方法的时候,我是迷惑的,为什么一会顺序,一会又并行,他们之间到底有什么区别呢?
AsSequential() 它的意思就是将后续的操作采用顺序处理,而不是继续并行执行,什么意思呢,就是比如有一个
操作要使用AsParallel()进行并行计算,但是后续的操作又要使用顺序执行,这个时候就要使用AsSequential()了.

AsOrdered()保证并行处理的结果按照输入数据的顺序排列,并不影响操作的并行执行.而AsSequential()将后续的操作转换为按顺序执行,但是不影响之前的并行处理,仅影响后续操作的执行顺序.

var nubers03 = Enumerable.Range(0, 10);
var result02 = nubers03.AsParallel().AsSequential().Where(num =>
{Console.WriteLine($"数据num03: {num},所在线程ID: {Thread.CurrentThread.ManagedThreadId}");Thread.Sleep(1000);return num % 2 == 0;
}).ToList();

上面这个例子在运行的时候,就是说按照顺序1秒打印一条日志,所以可以看出来在使用AsSequential()的时候是按照顺序执行的

#region 10. AsOrdered 和 AsSequentialvar numbers04 = Enumerable.Range(0, 10);
var queryOrdered = numbers04.AsParallel().AsOrdered().Select(num => num * num).Where(num =>{Thread.Sleep(1000);Console.WriteLine($"数据num04: {num},所在线程ID: {Thread.CurrentThread.ManagedThreadId}");return num % 2 == 0;}).ToList();
Console.WriteLine($"QueryOrderd: {string.Join(',', queryOrdered)}");
#endregion

在这里插入图片描述
然后AsOrdered()的执行结果可以看出来,它其实也是并行执行的,并且不能保证哪个数据先执行,只是它的结果是按照输入数据的顺序来进行生成的.

④ 线程的操作(创建_终止_挂起_恢复)
  • 线程的创建

1. 无参创建Thread,通过构造方法(委托)
2. 有参创建Thread,通过构造方法(有参委托)

Thread thread01 = new Thread(DoThread01);
Thread thread02 = new Thread(DoThread02);thread01.Start();
// 有参线程传递参数的方式
thread02.Start("Hello World!");
void DoThread01()
{Console.WriteLine("我是无参线程1,我正在运行!");
}
void DoThread02(object? obj)
{Console.WriteLine($"我是有参数的线程2,我的参数是{obj ?? "Null"},我正在运行.");
}
  • 线程等待阻塞

方法Join()

Join()方法的意思就是创建Join的线程会阻塞创建线程的执行,直到Join线程执行完毕,创建线程才会继续往下执行.
假如主线程创建了线程A,然后A.Join(),意思就是A会阻塞主线程的执行,主线程会等待A线程执行完毕之后才会继续往下执行.
如果没有A.join()主线程会继续往下执行,不会阻塞

Thread thread = new Thread(() =>
{for (int i = 0; i < 20; i++){Console.WriteLine($"线程Id: {Thread.CurrentThread.ManagedThreadId},执行For循环的 第 {i + 1} 次");Thread.Sleep(100);}
});
thread.Start();
thread.Join();
Console.WriteLine("主线程结束!");
  • 线程终止

Interrupt()终止线程

  1. Interrupt()方法用于中断线程的阻塞状态,引发ThreadInterruptedException异常,需要去捕获这个异常
  2. Interrrupt()方法需要终止的线程有类似IO或者是Sleep这种阻塞操作才可以,如果没有,比如写一个While(True)死循环,然后里面都是计算,这样Interrupt()被调用的时候,线程是没有事件去响应的,所以对中断的线程是有要求的
Thread thread = new Thread(() =>
{int startIndex = 1;try{while (true){Console.WriteLine($"线程: {Thread.CurrentThread.ManagedThreadId} 正在运行,第 {startIndex++} 次.. ");Thread.Sleep(200);}}catch (ThreadInterruptedException){Console.WriteLine($"线程: {Thread.CurrentThread.ManagedThreadId} 被终止");}
});
thread.Start();
Thread.Sleep(1000);
thread.Interrupt();
Console.WriteLine("主线程结束.");

现在加入我们将线程里面的Thread.Sleep(200)去除掉,那么会发现我们根本就无法终止这个线程(这里也是可以终止线程的,因为打印也是IO操作,所以我们把打印也去除掉,就来个运算将startIndex++)

Thread thread = new Thread(() =>
{int startIndex = 1;try{while (true){startIndex++;}}catch (ThreadInterruptedException){Console.WriteLine($"线程: {Thread.CurrentThread.ManagedThreadId} 被终止");}
});
thread.Start();
Thread.Sleep(1000);
thread.Interrupt();
Console.WriteLine("主线程结束.");

About()终止线程

为什么不推荐使用About()来终止线程?

  1. 使用About()来终止线程可能导致一些严重的问题,可能导致线程处于不确定状态.
  2. 在新版本的.NET版本中,About()方法别调用的时候可能会引发异常
  • 线程的挂起和恢复

之前的.NET中,使用SuspentResume方法用于挂起和恢复线程,但是这两个方法已经被标记为已过时,主要原因就是这些方法可能会导致线程死锁,死活锁等问题,推荐使用Monitor类的WaitPulse方法实现线程的挂起和恢复功能.Wait用于将当前线程挂起,
Pulse方法用于唤醒被挂起的线程.这种方式更加的安全,避免线程死锁问题.

bool IsPause = false;
object lockObj = new object();
Thread thread = new Thread(() =>
{lock (lockObj){int startIndex = 1;while (true){if (IsPause){Monitor.Wait(lockObj, 2000);}Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId}正在运行,index = {startIndex++}");Thread.Sleep(100);}}});
thread.Start();
Thread.Sleep(1000);
IsPause = true;
Thread.Sleep(2000);
IsPause = false;
lock (lockObj)
{Monitor.Pulse(lockObj);
}

注意一点就是在使用Monitor.Wait()方法和Monitor.Pulse()方法的时候要在lock语句块中,来保证线程同步和保证对象的状态的一致性

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

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

相关文章

IO流,文件操作

参考 Java IO 基础知识总结 | JavaGuide 史上最骚最全最详细的IO流教程&#xff0c;没有之一&#xff01; - 宜春 - 博客园 零、io-流简介 IO 即 Input/Output&#xff0c;输入和输出。数据输入到计算机内存的过程即输入&#xff0c;反之输出到外部存储&#xff08;比如数据…

Microsoft Outlook Lite 引入短信功能

随着科技的不断进步&#xff0c;我们的沟通方式也在不断演变。微软最新推出的 Outlook Lite 应用&#xff0c;不仅为我们提供了一个轻量级的电子邮件管理工具&#xff0c;现在更是带来了一项令人兴奋的新功能——短信服务。 Outlook Lite&#xff1a;轻量级&#xff0c;功能全…

手眼标定学习笔记

目录 标定代码&#xff1a; 手眼标定原理学习 什么是手眼标定 手眼标定的目的 eye in hand eye to hand AXXB问题的求解 标定代码&#xff1a; GitHub - pumpkin-ws/HandEyeCalib 推荐博文&#xff1a; https://zhuanlan.zhihu.com/p/486592374 手眼标定原理学习 参…

带DSP音效处理D类数字功放TAS5805M中文资料

国产替代D类数字功放中文资料访问下方链接 ACM8628 241W立体声182W单通道数字功放中文寄存器表 内置DSP多种音频处理效果ACM8628M-241W立体声或182W单通道数字功放 1 特性 具有增强处理能力和低功率损耗的 TAS5805M 23W、无电感器、数字输入、立体声、闭环 D 类音频放大器 …

Vue3中的常见组件通信之v-model

Vue3中的常见组件通信之v-model 概述 ​ 在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refs、parent、provide、inject、pinia、slot等。不同的组件关系用不同的传递方式。常见的撘配形式如下表所示。 组件关系传递方式父传子1. props2. v-model3. $r…

智能视频监控技术为游泳馆安全护航,助力安全管理新升级

随着社会的进步和科技的发展&#xff0c;视频监控技术在各行各业的应用越来越广泛。游泳馆作为公共场所&#xff0c;每天都会有大量的游泳者进出。在这样的环境中&#xff0c;有时难免会发生一些意外事故&#xff0c;如溺水、摔倒等。因此&#xff0c;视频监控建设的必要性尤为…

使用 MDC 实现日志链路跟踪,包教包会!

在微服务环境中&#xff0c;我们经常使用 Skywalking、Spring Cloud Sleut 等去实现整体请求链路的追踪&#xff0c;但是这个整体运维成本高&#xff0c;架构复杂&#xff0c;本次我们来使用 MDC 通过 Log 来实现一个轻量级的会话事务跟踪功能&#xff0c;需要的朋友可以参考一…

TCP 协议的相关特性

一些TCP协议的基础标志位&#xff1a; URG:紧急指针是否有效 ACK:确认号是否有效 PSH:提示接收端应用程序立刻把数据读走 RST:要求重新建立连接&#xff0c;也叫复位报文段 SYN:请求建立连接&#xff0c;同步报文段 FIN:通知要断开连接了我这里&#xff0c;结束报文段 一&#…

C++缺省参数函数重载

缺省参数 大家知道什么是备胎吗&#xff1f; C中函数的参数也可以配备胎。 3.1缺省参数概念 缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时&#xff0c;如果没有指定实参则采用该默认值&#xff0c;否则使用指定的实参。 void TestFunc(int a 0…

引擎:Shader

一、原理 创建Shader脚本&#xff0c;创建材质球&#xff0c;将物体的渲染效果Shader脚本挂载到材质球&#xff0c;最后把材质球挂到3d物体上面从而实现渲染。 二、模型边缘发光 原理&#xff1a;正对着摄像机的模型三角面边缘光最弱&#xff0c;垂直于摄像机的模型三角面边缘光…

opencv进阶 ——(九)图像处理之人脸修复祛马赛克算法CodeFormer

算法简介 CodeFormer是一种基于AI技术深度学习的人脸复原模型&#xff0c;由南洋理工大学和商汤科技联合研究中心联合开发&#xff0c;它能够接收模糊或马赛克图像作为输入&#xff0c;并生成更清晰的原始图像。算法源码地址&#xff1a;https://github.com/sczhou/CodeFormer…

什么是Spark RDD?(RDD的介绍与创建)

什么是Spark RDD&#xff1f;(RDD的介绍与创建) 一、RDD介绍 1、特点2、RDD的存储和指向3、RDD与DAG4、RDD的特性5、RDD分区6、RDD操作类型 二、RDD创建 1、引入必要的 Spark 库2、配置 Spark3、RDD创建4、示例代码 一、RDD介绍 RDD: 弹性分布式数据集&#xff08;Resilient…

Go微服务: 基于rocketmq:5.2.0搭建RocketMQ环境,以及示例参考

概述 参考最新官方文档&#xff1a;https://rocketmq.apache.org/zh/docs/quickStart/03quickstartWithDockercompose以及&#xff1a;https://rocketmq.apache.org/zh/docs/deploymentOperations/04Dashboard综合以上两个文档来搭建环境 搭建RocketMQ环境 1 ) 基于 docker-c…

K8S==ingress配置自签名证书

安装openssl Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions 生成证书 openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout example.local.key -out example.local.crt -subj "/CNexample.local/Oexample.local"创建K8S secr…

【JVM】已验鼎真,鉴定为:妈妈加载的(双亲委派模型)

【JVM】已验鼎真&#xff0c;鉴定为&#xff1a;妈妈加载的&#xff08;双亲委派模型&#xff09; 在Java的世界中&#xff0c;类加载器&#xff08;ClassLoader&#xff09;是Java虚拟机&#xff08;JVM&#xff09;用来动态加载类的基础组件。双亲委派模型&#xff08;Paren…

Java基础27,28(多线程,ThreadMethod ,线程安全问题,线程状态,线程池)

目录 一、多线程 1. 概述 2. 进程与线程 2.1 程序 2.2 进程 2.3 线程 2.4 进程与线程的区别 3. 线程基本概念 4.并发与并行 5. 线程的创建方式 方式一&#xff1a;继承Thread类 方式二&#xff1a;实现Runable接口 方式三&#xff1a;实现Callable接口 方式四&…

C#操作MySQL从入门到精通(10)——对查询数据进行通配符过滤

前言 我们有时候需要查询数据,并且这个数据包含某个字符串,这时候我们再使用where就无法实现了,所以mysql中提供了一种模糊查询机制,通过Like关键字来实现,下面进行详细介绍: 本次查询的表中数据如下: 1、使用(%)通配符 %通配符的作用是,表示任意字符出现任意次数…

【简单讲解TalkingData的数据统计】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

Python04:python代码设置作者/创建时间/文件名称

我们新建一个py文件时&#xff0c;如果希望文件开头有固定的内容&#xff0c;怎么设置呢&#xff1f; 比如代码作者、文件创建时间等。。。 1、点击左上角【Python】–>【Settings】设置 2、在弹出的新窗口找到【File and Code Templates】–>【Python Script】–>在右…

鸿蒙小案例-音乐播放器

之前参加鸿蒙比赛的音乐播放器 效果展示 HF音乐效果展示 功能列 有一些功能没写上去&#xff0c;自行发掘 说明&#xff1a; 1.API:网易云接口&#xff0c;QQ个人接口&#xff0c; 需要请看gitee 2.本地关系型数据由bug,提的工单已确认&#xff0c;建议使用API11,12,9的不稳…