C# 线程(1)

目录

        • 1 线程与进程
        • 2 创建线程
        • 3 线程等待
        • 4 线程优先级
        • 5 前台线程与后台线程
        • 6 Lock与线程安全
        • 7 Monitor
        • 8 死锁
        • 9 线程中异常处理

1 线程与进程

进程是计算机概念,一个程序运用时占用的的所有计算机资源(CPU、内存、硬盘、网络)统称为进程。
线程是操作系统中能够独立运行的最小单位,是进程(包含多个线程)中的一部分,线程也有自己的计算资源,多个线程间可以共享进程的资源
C#中的Thread其实是对计算机中线程概念的封装(API的封装),它的执行归根结底是向底层操作系统申请了线程资源。

在C#中的线程实现包括 Thread/ThreadPool/Task/Await Async
多线程的本质是资源换性能(CPU、内存、硬盘、网络)。好处是提高利用率(快) 坏处是耗费资源。但是在使用N个线程时,性能并不是成N倍的递增。
因为操作系统对CPU的调度时必须耗费些许时间,包括CPU的分片,线程的调度以及上下文的切换。同时CPU不够时降低线程的性能,这也意味着不是线程越多越好。

以下代码示例 默认引用了System.Threading命名空间

2 创建线程

线程创建 可以不通过显示的调用ThreadStart和ParameterizedThreadStart,主要是通过lamda表达式去创建线程

 static void Main(string[] args){Console.OutputEncoding = Encoding.UTF8;//public Thread(ThreadStart start) ThreadStart 一个无参数的委托Thread t1 = new Thread(() =>{Console.WriteLine("一个无参数的的线程创建");});t1.Start();//Thread(ParameterizedThreadStart start) ParameterizedThreadStart 一个有参数的委托//lambda 表达式是向线程传递数据的最强大的方法。然而必须小心,不要在启动线程之后误修改被捕获变量(captured variables).解决方法就是使用临时变量Thread t2 = new Thread((x) =>{Console.WriteLine("一个有参数的的线程创建{0}", x);});//传入参数t2.Start(100);}
3 线程等待

可以使用 Join()方法来等待其他线程完成任务

  public   class ThreadWork{public void Method1() {Console.OutputEncoding = Encoding.UTF8;Console.WriteLine("当前线程名称" + Thread.CurrentThread.Name);for (int i = 0; i < 10; i++) {Thread.Sleep(1000);//线程暂停Console.WriteLine(i);}}}     public class Program{static void Main(string[] args){ThreadWork work = new ThreadWork();Thread t = new Thread(work.Method1);t.Start();t.Join();//主线程等待子线程完成任务Console.WriteLine("Finish");}
}
4 线程优先级

线程的Priority属性决定了相对于操作系统中的其它活动线程,它可以获得多少执行时间。线程优先级的取值如下:
enum ThreadPriority { Lowest, BelowNormal, Normal, AboveNormal, Highest } 。只有当多个线程同时活动时,线程优先级才有意义。默认创建的线程优先级别为Normal

  public   class ThreadWork{public void Method1() {Console.OutputEncoding = Encoding.UTF8;Console.WriteLine("当前线程名称" + Thread.CurrentThread.Name);for (int i = 0; i < 10; i++) {Thread.Sleep(1000);//线程暂停Console.WriteLine(i);}}}
      static void Main(string[] args){ThreadWork work = new ThreadWork();Thread t1 = new Thread(work.Method1) { Priority = ThreadPriority.Highest, Name = "t1" };Thread t2 = new Thread(work.Method1) { Priority = ThreadPriority.Normal, Name = "t2" };t2.Start(); //t1线程优先级别高于t2线程,将优先获得CPU的时间片,先执行t1.Start();Console.WriteLine("Finish");}
5 前台线程与后台线程

可以通过设置IsBackground来设定线程是否为后台线程 。默认显示创建的线程都是前台线程。前台线程与后台线程最大的区别就是 进程会等待所有的前台线程,当前台线程全部结束后,尽管后台线程的任务没有执行完,进程会自动结束所有存在的后台线程。所以当程式定义了一个永远不会完成的前台线程时,进程将永远不会结束。线程的前台/后台状态与它的优先级和执行时间的分配无关

  public   class ThreadWork{public void Method2(int num) {Console.OutputEncoding = Encoding.UTF8;for (int i = 0; i < num; i++){Thread.Sleep(1000);//线程暂停Console.WriteLine("当前线程名称{0} 打印数据{1}",  Thread.CurrentThread.Name,i);}}}
       static void Main(string[] args){ThreadWork work = new ThreadWork();Thread t1 = new Thread(() => work.Method2(5)) { Name = "t1" };Thread t2 = new Thread(() => work.Method2(10)) { Name = "t2", IsBackground = true };t1.Start();t2.Start(); // t1线程为前台线程(执行时间>10s) t2线程为后台线程(执行时间>5s) 前台线程结束后,会中断t2线程Console.WriteLine("Finish");}
6 Lock与线程安全

竞态: 字面意思是竞争,并发的执行单元对共享资源(硬件资源和软件上的全局变量,静态变量等)的访问容易发生竞态,竞态是出现线程不安全的重要因素。
为了解决多线程并发抢夺共享资源的问题,可采用lock 关键字,lock会锁定最先抢到资源的线程,并且会阻塞(不占用CPU资源)其他试图抢夺该资源的其他线程,直到lock锁定的线程释放这个锁资源
这样就确保了在同一时刻只有一个线程能进入临界区(critical section,不允许并发执行的代码)像这种用来避免在多线程下的不确定性的方式被称为线程安全(thread-safe)
尽管lock关键字的存在会解决线程安全的问题,但是运用不恰当,存在严重的性能问题,因为上下切换上下文的时间可能会比单个线程执行还要慢。

   public  class CounterBase{//多线程共享这个变量,当存在读写操作时,会出现线程不安全的问题public int num { get; private set; }public CounterBase(int num){this.num = num;}public void DecrementWithLock(){Console.WriteLine(Thread.CurrentThread.Name);//使用lock关键字,标识这段方法只允许一个线程持有lock (this) {num--;}}public void IncrementWithLock(){Console.WriteLine(Thread.CurrentThread.Name);lock (this){num++;}}public void Count (){for (int i = 0; i < 10; i++){IncrementWithLock();DecrementWithLock();}}}
 static void Main(string[] args){CounterBase counter = new CounterBase(100);Thread t1 = new Thread(() => counter.Count()) { Name = "t1" }; // t1、t2 线程共享counter实例,并同时执行CountWhihLock()方法。意味着此时出现了竞态现象,容易出现线程不安全的问题 Thread t2 = new Thread(() => counter.Count()) { Name = "t2" };//多线程并行t1.Start();t2.Start();t1.Join();t2.Join();Console.WriteLine("Finish");Console.WriteLine(counter.num); //100}
7 Monitor

在Lock与线程安全这一section中,用到lock关键字来确保代码屏障区(用lock关键字包裹的代码区,同一时间,只有一个线程能进入到代码中)的安全执行 。lock关键字Monitor类的的一个语法糖。可以通过TryEnter()进入锁 Monitor.Exit()进出锁。上述示例可以用Monitor类来重构功能

   public  class CounterBase{//多线程共享这个变量,当存在读写操作时,会出现线程不安全的问题public int num { get; private set; }public CounterBase(int num){this.num = num;}public void DecrementWithLock(){Console.WriteLine(Thread.CurrentThread.Name);//使用Monitor替换lock关键字Monitor.TryEnter();num++;Monitor.Exit();}public void IncrementWithLock(){Console.WriteLine(Thread.CurrentThread.Name);//使用Monitor替换lock关键字Monitor.TryEnter();num--;Monitor.Exit();}public void Count (){for (int i = 0; i < 10; i++){IncrementWithLock();DecrementWithLock();}}}
8 死锁

死锁 是多个线程等待永远拿不到的锁资源时 出现的程式异常问题。线程处于阻塞状态中。

    public class DeadLockWork{private readonly object _lock1 = new object();private readonly object _lock2 = new object();public void Method1() {lock (_lock1) {Console.WriteLine(Thread.CurrentThread.Name+" enterd Method1");Thread.Sleep(10);lock (_lock2) {}}}public void Method2(){lock (_lock2){Console.WriteLine(Thread.CurrentThread.Name + " enterd Method2");Thread.Sleep(10);lock (_lock1){}}}}
        static void Main(string[] args){DeadLockWork deadLock = new DeadLockWork();// t1、t2 线程共享deadLock实例Thread t1 = new Thread(() => deadLock.Method1()) { Name = "t1" };Thread t2 = new Thread(() => deadLock.Method2()) { Name = "t2" };t1.Start();//t1线程进入方法后,持有_lock1锁,暂停10ms后尝试获取_lock2锁t2.Start();//t2线程进入方法后,持有_lock2锁,暂停10ms后尝试获取_lock1锁//主线程等待t1、t2线程完成任务t1.Join();t2.Join();//由于lock关键字会阻塞其他正在尝试获取锁的线程,t1持有lock1并尝试获取_lock2锁,t2持有lock2并尝试获取_lock1锁。//t1永远拿不到_lock2锁,t2永远拿不到_lock1锁。导致线程死锁。//进程等待两个前台线程执行完成,死锁导致程式永远不会结束Console.WriteLine("Finish");}
9 线程中异常处理

在实际项目中,每一个线程中应该有异常处理的逻辑,
因为每个线程都独享栈空间,主线程无法捕获其他工作线程中抛出的异常。未被捕获的异常可能会导致程式的异常中断。为了加强程式的健壮性,应该在工作任务中设计异常处理的逻辑

 static void Main(string[] args){Console.OutputEncoding = Encoding.UTF8;Thread t1 = new Thread(() =>{try{Console.WriteLine("I am Thread " + Thread.CurrentThread.Name);throw new Exception("模拟工作的中的异常:端口被占用");}catch (Exception e){//捕获线程中的异常Console.WriteLine(e.Message);}}){ Name = "t1" };t1.Start();}

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

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

相关文章

Apache Mina SSHD 和 JSch

最近写了7篇关于Java操作远程服务器文件的上传下载和远程服务器命令执行的相关文章&#xff0c;主要使用到了Apache Mina SSHD 和 JSch 两个组件&#xff0c;更多文章细节实现参考&#xff1a; JSch实践&#xff08;一&#xff09;简介和远程文件的操作 JSch实践&#xff08;…

额,收到阿里云给的赔偿了!

众所周知&#xff0c;就在刚过去不久的11月12号&#xff0c;阿里云突发了一次大规模故障&#xff0c;影响甚广。 以至于连咱们这里评论区小伙伴学校的洗衣机都崩了&#xff08;手动doge&#xff09;。 这么关键的双11节点&#xff0c;这么多热门业务和产品&#xff0c;这么大规…

Django如何设置时区为北京时间?

Django默认使用的是UTC时间&#xff0c;北京时间比UTC早8个小时&#xff0c;即如果UTC是凌晨两点&#xff0c;那么北京时间是早上八点。 Django中把setting.py中的语句&#xff1a; TIME_ZONE UTC修改为&#xff1a; TIME_ZONE Asia/Shanghai就把时区改为了北京时间。 这…

Linux如何查找某个路径下大于1G的文件

find 命令可以用于在 Linux 或 macOS 系统中查找文件和目录。如果你想查找大于1GB的文件&#xff0c;可以使用 -size 选项结合 参数。以下是一个示例&#xff1a; find /path/to/search -type f -size 1G这里的 /path/to/search 是你要搜索的目录的路径。这个命令将查找该目录…

Unity3d 灯光阴影开启,法线贴图出现BUG

URP项目打开灯光的阴影后&#xff0c;法线贴图出现BUG 解决方案&#xff1a;按照下图所示调整材质的选项即可

XML Schema中的attributeFormDefault

XML Schema中的attributeFormDefault属性&#xff0c;用以指定元素的属性默认是否必须带有命名空间前缀。 attributeFormDefault属性可以取值qualified或unqualified&#xff0c;默认值是unqualified。 当取值为qualified时&#xff0c;表示属性必须用命名空间作为前缀&#x…

精力管理金字塔

精力管理金字塔 由协和医学院的张遇升博士在《掌控精力&#xff1a;不疲惫的身心管理术》一书中提出&#xff0c;分层次对精力管理提出了解析和有效的建议。 模型介绍 精力管理是一个可以学会的技能&#xff0c;学会了科学的精力管理方法&#xff0c;就能使自己的精力越来越好…

股票技术从初级到高级,从实盘进阶到摩尔缠论

一、教程描述 摩尔缠论是什么&#xff1f;一个伟大的缠论分支体系&#xff0c;由顶尖高手创立的缠论分支&#xff0c;这个顶尖高手&#xff0c;江湖上的代号为摩尔&#xff0c;可能是一个人&#xff0c;或者是一群人。摩尔缠论&#xff0c;基于缠论的核心思想与基础理论&#…

面试必须要知道的MySQL知识--索引

10 索引 10.1 数据页存储结构 10.1.1 数据页的各个部分 在讲索引之前&#xff0c;让我们看看一个单独的数据页是什么样子的 去除掉一些我们不太需要那么关注的部分后&#xff0c;简化如下&#xff1a; 也就是说平时我们在一个表里插入的一行一行的数据会存储在数据页里&#…

Leetcode算法系列| 3. 无重复字符的最长子串

目录 1.题目2.题解C# 解法一&#xff1a;滑动窗口算法C# 解法二&#xff1a;索引寻找Java 解法一&#xff1a;滑动窗口算法Java 解法二&#xff1a;遍历字符串 1.题目 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例1: 输入: s "ab…

数据结构——链式二叉树的实现(详解)

呀哈喽。我是结衣。 不知道大家的递归学到怎么样呢&#xff1f;如果大家的递归功底不是很好&#xff0c;那么我相信在学完这篇文章后大家一定会对递归有一个更深层次的了解的。 构造链式二叉树 在学习二叉树的基本操作前&#xff0c;需先要创建一棵二叉树&#xff0c;然后才能…

直播场景视频和特效解决方案

直播已经成为企业与消费者互动的重要方式&#xff0c;如何提供优质的直播内容&#xff0c;提升直播效果&#xff0c;以及实现直播内容的商业化转化&#xff0c;一直是企业面临的重要挑战。为此&#xff0c;美摄科技提供了一套全面的直播场景解决方案&#xff0c;帮助企业解决这…

爬虫学习 异步爬虫(五)

多线程 多进程 协程 进程 运行中的程序 线程 被CPU调度的执行过程,操作系统 运算调度的min单位 在进程之中,进程中实际运作单位 from threading import Thread#创建任务 def func(name):for i in range(100):print(name,i)if __name__ __main__:#创建线程t1 Thread(target …

0 NLP: 数据获取与EDA

0数据准备与分析 二分类任务&#xff0c;正负样本共计6W&#xff1b; 数据集下载 https://github.com/SophonPlus/ChineseNlpCorpus/raw/master/datasets/online_shopping_10_cats/online_shopping_10_cats.zip 样本的分布 正负样本中评论字段的长度 &#xff0c;超过500的都…

Java基本类型包装类

文章目录 前言创建包装类对象包装类数据类型转换 前言 为什么要了解包装类呢&#xff1f;因为在Java中有一句很经典的话&#xff0c;万物皆对象。Java中的8种基本数据类型还不是对象&#xff0c;所以要把它们变成对象&#xff0c;变成对象之后&#xff0c;可以提供一些方法对数…

uniapp 导航分类

商品分类数据&#xff0c;包括分类名称和对应的商品列表点击弹出 列表的内容 展示效果如下&#xff1a; 代码展示 ①div部分 <view class"container"><view class"menu-bar"><view class"menu"><view class"menu-sc…

【hive-design】hive架构详解:描述了hive架构,hive主要组件的作用、hsql在hive执行过程中的底层细节、hive各组件作用

文章目录 一. Hive Architecture二. Metastore1. Metastore Architecture2. Metastore Interface 三. Compiler四. hive架构小结 本文主要讨论了 描述了hive架构&#xff0c;hive主要组件的作用详细描述了hsql在hive执行过程中的底层细节描述了hive各组件作用 一. Hive Archite…

Kubernetes入门篇

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容Kubernetes是什么&#xff1f;为什么需要 Kubernetes&#xff0c;它能做什么&#xff1f;Kubernetes 版本进化Kubernetes 概念Kubernetes 中 3 个重要概念 &#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介…

第四阶|自在行草 暄桐教室,林曦书法 从书法之美到生活之美

我这有很多的课程&#xff0c;需要了可以取用 新一期&#xff08;入门课&#xff09;&#xff0c;目前已经更新完毕。 新一期&#xff08;第一阶&#xff09;&#xff0c;目前已经更新完毕。 新一期&#xff08;第二阶&#xff09;&#xff0c;目前已经更新完毕。 新一期&#…

验证回文串(双指针)

LCR 018. 验证回文串 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定一个字符串 s &#xff0c;验证 s 是否是 回文串 &#xff0c;只考虑字母和数字字符&#xff0c;可以忽略字母的大小写。 本题中&#xff0c;将空字符串定义为有效的 回文串 。 样例输入 示例 1…