C# 多线程

概述

进程和线程

进程:指在系统中运行的一个应用程序。

线程:进程中的一个执行任务。一个进程至少有一个线程,一个进程可以有多个线程,多个线程可共享数据。

多线程

多线程:在一个程序中同时运行多个线程,每个线程执行各自的任务。

优点:使用多线程可以提高应用程序的响应能力,并利用多处理器或多核系统提高应用程序吞吐量。

缺点:死锁和争用条件

多线程适用场景:任务执行比较耗时的情况,也可以解决一些非常耗时的且长时间占用cpu资源的程序。

多线程的特点:

1、运行顺序不确定。

2、线程之间平行执行。

前台线程和后台线程

前台线程必须全部执行完,即使主线程关闭掉,这时进程仍然存活。后台线程在未执行完成时,如果前台线程关掉,则后台线程也会停掉。后台线程会随着主线程的关闭,而自动关闭。

补充

1、新创建的Thread默认是前台线程,可以通过设置IsBackground属性将其改为后台线程

2、线程池中的线程是后台线程

3、Task开启的线程是后台线程

4、前台线程适用场合:重要核心的,或者需要长时间等待的任务,例如:UI界面线程、发送数据的线程

5、后台线程适用:非核心且用于处理时间较短的任务适用。

Thread

Thread开启的线程默认都是后台线程

开启线程

//命名空间
using System.Threading;Thread thread = new Thread(SayHi);
void SayHi()
{Thread.Sleep(3000);Console.WriteLine("Hello World!");
}
thread.Start();

线程传参

//传递单个参数,thread.Start只支持传一个参数
Thread thread = new Thread((fileName) =>
{Console.WriteLine($"正在下载的文件名是{fileName}");
});
thread.Start("原神.apk");//传递多个参数   定义一个专门类,通过构造函数传参
//自定义类
class WriteInfo
{private string _name;private int _age;public WriteInfo(string name, int age){_name = name;_age = age;}public void ShowHumanInfo(){Console.WriteLine($"我叫{_name},今年{_age}岁");}
}public void ShowInfo(string name, int age)
{WriteInfo info = new WriteInfo(name, age);Thread t = new Thread(info.ShowHumanInfo);t.Start();
}

常用属性

属性

描述

CurrentThread

获取当前正在运行的线程

IsAlive

获取当前线程的执行状态

IsBackground

某个线程是否为后台线程

IsThreadPoolThread

线程是否属于托管线程池

ManagedThreadId

获取当前托管线程的唯一标识符

Name

获取或设置线程的名称

Priority

线程的优先级可以影响线程的调用顺序

ThreadState

当前线程的状态

线程状态

Aborted

线程状态包括 AbortRequested 并且该线程现在已死,但其状态尚未更改为 Stopped

AbortRequested

已对线程调用了 Abort(Object) 方法,但线程尚未收到试图终止它的挂起的 ThreadAbortException

Background

线程正作为后台线程执行。 此状态可以通过设置 IsBackground 属性来控制

Running

线程已启动且尚未停止

Stopped

线程已停止

StopRequested

正在请求线程停止

Suspended

线程已挂起

SuspendRequested

正在请求线程挂起

Unstarted

尚未对线程调用 Start() 方法

WaitSleepJoin

线程已被阻止。 这可能是调用 Sleep(Int32) 或 Join()、请求锁定或在线程同步对象上等待的结果

常用方法

方法

描述

Start

开启线程

Abort

终止线程

Sleep

暂停线程一段时间

Join

阻塞调用线程,直到某个线程终止

例子:等待Thread线程完成后进行后续操作

// 创建并启动新线程
Thread newThread = new Thread(() =>{Console.WriteLine("子线程开始运行");// 模拟耗时操作Thread.Sleep(2000);Console.WriteLine("子线程结束运行");});newThread.Start();// 等待子线程结束
newThread.Join();Console.WriteLine("主线程结束");

ThreadPool 线程池

1、线程池创建的线程默认都是后台线程,不能把池中的线程修改为前台线程,也不能修改线程池中优先级与名称。

2、线程池中的线程只能用于时间比较短的任务,如果后台线程需要长时间运行,则需要单独开启,不适合用线程池。

3、手动创建多个Thread线程可能会消耗较多性能,通过线程池可以提高效率。 

4、缺点

ThreadPool不支持线程的取消、完成、失败通知等操作;

ThreadPool不支持线程执行的先后次序;

void ThreadPoolTest()
{ThreadPool.QueueUserWorkItem(GenFile, "原神.apk");ThreadPool.QueueUserWorkItem(GenFile, "王者荣耀.apk");ThreadPool.QueueUserWorkItem(GenFile, "蛋仔排队.apk");ThreadPool.QueueUserWorkItem(GenFile, "炉石传说.apk");
}void GenFile(object fileName)
{Thread.Sleep(3000);Console.WriteLine($"生成了文件{fileName}.txt");
}

Task 任务

Task开启的线程是后台线程

开启线程

//第一种方式
Task task = new Task(() =>
{});
task.Start();//第二种方式 Task.Run
Task task = Task.Run(() =>
{});//第三种方式 TaskFactory
TaskFactory taskFactory = new TaskFactory();
Task task = taskFactory.StartNew(() =>
{});//第四种方式 Task.Factory
Task task = Task.Factory.StartNew(() =>
{});

常用方法

方法

描述

Wait

等待Task完成执行

WaitAny

等待列表中任一Task完成执行,同步方法,会阻塞主线程

WaitAll

等待列表中所有Task完成执行,同步方法,会阻塞主线程

WhenAny

创建一个任务,该任务将在任一提供的任务完成时完成。异步方法,不会阻塞主线程

WhenAll

创建一个任务,该任务将在数组中的所有 Task 对象完成时完成。异步方法,不会阻塞主线程

ContinueWith

在任务完成后回调一个延续任务,参数是调用方的任务信息

//并行运行多个任务,等待任务都运行完后添加延续事件逻辑
List<Task> list = new List<Task>();
list.Add(Task.Run(() =>
{Console.WriteLine("开始做菜:");Thread.Sleep(3000);Console.WriteLine("做好素菜了!");}));list.Add(Task.Run(() =>
{Console.WriteLine("开始做菜:");Thread.Sleep(5000);Console.WriteLine("做好荤菜了!");}));Task.WhenAll(list).ContinueWith(t =>
{Console.WriteLine("菜都做好了,开饭吧!");});

WaitAll和WhenAll的区别?

1、Task.WaitAll 是一种同步方法,它会阻塞调用线程,直到所有提供的任务都已完成。当需要确保一组任务在继续之前已完成时,该方法很有用,但它以阻塞方式执行,这意味着调用 Task.WaitAll 的线程会被占用,直到所有任务都完成为止。

2、Task.WhenAll 是一种异步方法,当所有提供的任务都完成后,该方法将返回单个任务。与 Task.WaitAll 不同,它不会阻止调用线程。相反,它允许调用代码继续异步执行。

async/await + Task

async void TestAsync()
{await Task.Run(() =>{Console.WriteLine("开始做菜:");Thread.Sleep(3000);Console.WriteLine("做好素菜了!");});Console.WriteLine("开饭了!");
}

多线程隐患

争用条件

程序的结果取决于两个或更多个线程中的哪一个先到达某一特定代码块时出现的一种 bug。 多次运行程序会产生不同的结果,并且无法预测任何给定运行的结果。

public int gameState; 
void DeadLock(){Thread t1 = new Thread(ChangeMyState);Thread t2 = new Thread(ChangeMyState);t1.Start();t2.Start();}void ChangeMyState(){while (true){gameState++;if (gameState == 100){Console.WriteLine("啊哦,好像出现问题了");}gameState = 100;}}

解决方案:用 lock 语句锁定在线程中共享的资源。

public int gameState;
public object objLock = new object();
void DeadLock()
{Thread t1 = new Thread(ChangeMyState);Thread t2 = new Thread(ChangeMyState);t1.Start();t2.Start();
}void ChangeMyState()
{lock (objLock){while (true){gameState++;if (gameState == 100){Console.WriteLine("啊哦,好像出现问题了");}gameState = 100;}}
}

死锁

如果使用lock不当,就会产生死锁情况!

描述:两个线程中的每一个线程都尝试锁定另外一个线程已锁定的资源时,就会发生死锁,两个线程都不能继续执行。(公共资源被多个线程争抢,导致一个线程永远等待另一个线程释放资源的异常情况)

死锁的四个必要条件
1.互斥条件:资源一次只能被一个线程占用。

2.持有并等待条件:线程持有一个资源并等待获取其他资源。

3.不可剥夺条件:线程已获得的资源条件不能被强行剥夺,只能由线程自己释放。

4.循环等待条件:存在一组线程,每个线程都在等待下一线程持有的资源,形成一个环形等待。

死锁问题复现

 object resourceA = new object();object resourceB = new object();int result;public void CheckDeadlock(){var thread1 = new Thread(Logic1);var thread2 = new Thread(Logic2);thread1.Start();thread2.Start();thread1.Join(); thread2.Join();Console.WriteLine(result);}public void Logic1(){lock (resourceA){Thread.Sleep(100);lock (resourceB){result += 1;}}}public void Logic2(){lock (resourceB){Thread.Sleep(100);lock (resourceA){result += 2;}}}

解决方案

方法一 调整锁的顺序

确保所有线程按相同的顺序请求锁。这可以打破死锁的循环等待条件。只要所有的线程都以相同的顺序请求资源,死锁就不会发生。

public void Logic1()
{lock (resourceA){Thread.Sleep(100);lock (resourceB){result += 1;}}
}public void Logic2()
{lock (resourceA){Thread.Sleep(100);lock (resourceB){result += 2;}}
}

方法二 锁的超时机制

使用Monitor.TryEnter 来设置获取锁的超时时间。如果超过指定时间无法获取锁,线程可以退出或执行其他操作。

public void Thread1Work()
{if (Monitor.TryEnter(resourceA, TimeSpan.FromSeconds(1)))  // 尝试获取锁1,超时时间1秒{try{Console.WriteLine("Thread 1 acquired lock1");Thread.Sleep(100);if (Monitor.TryEnter(resourceB, TimeSpan.FromSeconds(1)))  // 尝试获取锁2,超时时间1秒{try{result += 1;}finally{Monitor.Exit(resourceB);  // 释放锁2}}else{Console.WriteLine("Thread 1 failed to acquire lock2, potential deadlock detected.");}}finally{Monitor.Exit(resourceA);  // 释放锁1}}else{Console.WriteLine("Thread 1 failed to acquire lock1, potential deadlock detected.");}
}public void Thread2Work()
{if (Monitor.TryEnter(resourceB, TimeSpan.FromSeconds(1)))  // 尝试获取锁2,超时时间1秒{try{Thread.Sleep(100);if (Monitor.TryEnter(resourceA, TimeSpan.FromSeconds(1)))  // 尝试获取锁1,超时时间1秒{try{result += 2;}finally{Monitor.Exit(resourceA);  // 释放锁1}}else{Console.WriteLine("Thread 2 failed to acquire lock1, potential deadlock detected.");}}finally{Monitor.Exit(resourceB);  // 释放锁2}}else{Console.WriteLine("Thread 2 failed to acquire lock2, potential deadlock detected.");}
}

方法三 减少锁的持有时间

尽量缩小锁定的范围,确保只有在修改共享资源时才持有锁。这样可以减少锁竞争,降低死锁发生的机率。

参考链接

一文搞懂C#多线程、并发、异步、同步、并行 - biubiu12138 - 博客园

C# Task详解 - 五维思考 - 博客园

同步与异步:.NET 中的 Task.WaitAll 和 Task.WhenAll-CSDN博客

死锁(Deadlock)C#_c#死锁的原因及解决方法-CSDN博客

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

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

相关文章

突破光学成像局限:全视野光学血管造影技术新进展

全视野光学血管造影&#xff08;FFOA&#xff09;作为一种实时、无创的成像技术&#xff0c;能够提取生物血液微循环信息&#xff0c;为深入探究生物组织的功能和病理变化提供关键数据。然而&#xff0c;传统FFOA成像方法受到光学镜头景深&#xff08;DOF&#xff09;的限制&am…

Deepgram推出Nova-3 Medical,AI语音转录助力医疗行业

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

centOS 环境 安装redis方法

一、准备centOS环境 参考文章&#xff1a;Hyper-V 安装CentOS7_代码草率了的博客-CSDN博客 二、redis官网 地址&#xff1a;Download | Redis 演示版本为?redis-5.0.14.tar.gz 三、redis源码编译 登录后创建soft目录 进入目录使用wget下载所需资源包 命令&#xff1a;w…

[51 单片机] --串口编程

1&#xff0c;通讯方式基本概念 1&#xff0c;按照 --> 数据传送方式串行通讯&#xff1a;使用一条数据线&#xff0c;将数据一位一位地依次传输&#xff0c;每一位数据占据一个固定的时间长度&#xff0c;串行通信的特点&#xff1a;传输线少&#xff0c;长距离传送时成本…

Golang的微服务服务发现机制

## 1. Golang微服务服务发现机制 微服务架构已经成为当今软件开发的主流趋势&#xff0c;它能将复杂的单体应用拆分成小而独立的服务单元&#xff0c;实现更快的开发、部署和扩展。在微服务架构中&#xff0c;服务发现是非常重要的一环&#xff0c;它能够实现服务之间的自动发现…

Python 创建地形图

原始地 DEM。 火山口湖 (OR) 区域的起始 DEM。数据来自 NASA DEM 本身非常美丽&#xff0c;但我们先进行分层。 将自定义色彩图应用于 DEM 对于我在 ArcGIS Pro 版本中所做的初始高程样式着色&#xff0c;我使用了“高程 #7”。在 matplotlib 中可用的标准颜色图中&#xff…

《Operating System Concepts》阅读笔记:p180-p187

《Operating System Concepts》学习第 20 天&#xff0c;p180-p187 总结&#xff0c;总计 8 页。 一、技术总结 1.forke-join A strategy for thread creation in which the main parent thread creates (forks) one or more child threads and then waits for the children…

文心4.5,大模型下半场的野心之作

2025年开年&#xff0c;全球大模型竞赛进入白热化阶段。2月28日&#xff0c;百度宣布其文心大模型4.5将于3月16日正式上线&#xff0c;强调其原生多模态与深度思考能力&#xff0c;并计划于6月30日开源。这一动作不仅标志着百度技术路线的重大转向&#xff0c;更被视为中国大模…

transformer架构解析{前馈全连接层,规范化层,子层(残差)连接结构}(含代码)-4

目录 前言 前馈全连接层 学习目标 什么是前馈全连接层 前馈全连接层的作用 前馈全连接层代码实现 规范化层 学习目标 规范化层的作用 规范化层的代码实现 子层&#xff08;残差&#xff09;连接结构 学习目标 什么是子层&#xff08;残差&#xff09;连接结构 子层连…

Django视图与URLs路由详解

在Django Web框架中&#xff0c;视图&#xff08;Views&#xff09;和URLs路由&#xff08;URL routing&#xff09;是Web应用开发的核心概念。它们共同负责将用户的请求映射到相应的Python函数&#xff0c;并返回适当的响应。本篇博客将深入探讨Django的视图和URLs路由系统&am…

串口通讯基础

第1章 串口的发送和接收过程 1.1 串口接收过程 当上位机给串口发送(0x55)数据时&#xff0c;MCU的RX引脚接受到&#xff08;0x55&#xff09;数据&#xff0c;数据(0x55)首先进入移位寄存器。数据全部进入移位寄存器后&#xff0c;一次将&#xff08;0x55&#xff09;全部搬运…

kakfa-3:ISR机制、HWLEO、生产者、消费者、核心参数负载均衡

1. kafka内核原理 1.1 ISR机制 光是依靠多副本机制能保证Kafka的高可用性&#xff0c;但是能保证数据不丢失吗&#xff1f;不行&#xff0c;因为如果leader宕机&#xff0c;但是leader的数据还没同步到follower上去&#xff0c;此时即使选举了follower作为新的leader&#xff…

基于Linux系统的物联网智能终端

背景 产品研发和项目研发有什么区别&#xff1f;一个令人发指的问题&#xff0c;刚开始工作时项目开发居多&#xff0c;认为项目开发和产品开发区别不大&#xff0c;待后来随着自身能力的提升&#xff0c;逐步感到要开发一个好产品还是比较难的&#xff0c;我认为项目开发的目的…

STM32——DMA详解

目录 一&#xff1a;DMA简介 二&#xff1a;DMA基本结构 三&#xff1a;DMA实现过程 1.框图 2.DMA进行转运的条件 四&#xff1a;函数 一&#xff1a;DMA简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设存储器或者存储器和存储器之间的高速数据传输&…

告别卡顿,拥抱流畅!MemReduct——内存清理工具

先给安装包下载地址&#xff1a;MemReduct.exe下载&#xff0c;无脑下一步安装即可。 MemReduct 是一款出色的内存清理工具&#xff0c;以下是对它的详细介绍&#xff1a; 功能特点 高效内存清理&#xff1a;采用先进算法及系统底层 API&#xff0c;能智能清理系统缓存、应用…

告别GitHub连不上!一分钟快速访问方案

一、当GitHub抽风时&#xff0c;你是否也这样崩溃过&#xff1f; &#x1f621; npm install卡在node-sass半小时不动&#x1f62d; git clone到90%突然fatal: early EOF&#x1f92c; 改了半天hosts文件&#xff0c;第二天又失效了... 根本原因&#xff1a;传统代理需要复杂…

指纹细节提取(Matlab实现)

指纹细节提取概述指纹作为人体生物特征识别领域中应用最为广泛的特征之一&#xff0c;具有独特性、稳定性和便利性。指纹细节特征对于指纹识别的准确性和可靠性起着关键作用。指纹细节提取&#xff0c;即从指纹图像中精确地提取出能够表征指纹唯一性的关键特征点&#xff0c;是…

【对话推荐系统综述】A Survey on Conversational Recommender Systems

文章信息&#xff1a; 发表于&#xff1a;ACM Computing Surveys 2021 原文链接&#xff1a;https://arxiv.org/abs/2004.00646 Abstract 推荐系统是一类软件应用程序&#xff0c;旨在帮助用户在信息过载的情况下找到感兴趣的项目。当前的研究通常假设一种一次性交互范式&am…

【0001】初识Java

Java是世界上最好的语言&#xff0c;没有之一&#xff01;&#xff01;&#xff01; Java是世界上最好的语言&#xff0c;没有之一&#xff01;&#xff01;&#xff01; Java是世界上最好的语言&#xff0c;没有之一&#xff01;&#xff01;&#xff01; 重要的事情说三遍&am…

全向广播扬声器在油气田中的关键应用 全方位守护安全

油气田作为高风险作业场所&#xff0c;安全生产始终是重中之重。在紧急情况下&#xff0c;如何快速、有效地传达信息&#xff0c;确保人员安全撤离&#xff0c;是油气田安全管理的关键环节。全向广播扬声器凭借其全方位覆盖、高音质输出和强大的环境适应性&#xff0c;成为油气…