C# 线程基础 二

news2024/11/18 8:17:23

目录

八、前台线程和后台线程

九、线程参数的传递

十、线程中的 lock 关键字

十一、Monitor类锁定

结束


八、前台线程和后台线程

默认情况下,显式创建的线程是前台线程,通过手动的设置 Thread 类的属性 IsBackground = true 来指示当前线程为一个后台线程。

前台线程和后台线程最主要的区别是,所有的前台线程执行完成后,后台线程会自动停止工作。

注意事项:

不管是前台线程还是后台线程,如果线程内出现了异常,都会导致进程的终止。

托管线程池中的线程都是后台线程,使用 new Thread 方式创建的线程默认都是前台线程。

代码:

using System;
using System.Threading;

namespace Test5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var sample1 = new ThreadSample(10);
            var sample2 = new ThreadSample(20);

            var thread1 = new Thread(sample1.CountNum);
            thread1.Name = "thread1";

            var thread2 = new Thread(sample1.CountNum);
            thread2.Name = "thread2";
            thread2.IsBackground = true;

            thread1.Start();
            thread2.Start();

            Console.ReadKey();
        }
    }

    class ThreadSample
    {
        private readonly int iterations;

        public void CountNum()
        {
            for (int i = 0; i < iterations; i++)
            {
                Thread.Sleep(500);
                Console.WriteLine("线程名:{0}, i:{1}", Thread.CurrentThread.Name, i);
            }
        }

        public ThreadSample(int iterations)
        {
            this.iterations = iterations;
        }
    }
}

运行:

在上面的代码中可以看到定义如下

var sample1 = new ThreadSample(10);
var sample2 = new ThreadSample(20);

线程2的执行次数是20次,但是在打印中只执行了9次,线程2执行到第9次后,就停止运行了,因为这时候,前台线程已经执行完毕,后台线程也就跟随着一起结束了。

九、线程参数的传递

在线程的 Start 方法中,其实还有一个重载函数的,可以向线程执行的方法中传递一个参数,参考微软官方的代码:

[MethodImpl(MethodImplOptions.NoInlining)]
[HostProtection(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
public void Start(object parameter)
{
    if (m_Delegate is ThreadStart)
    {
        throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ThreadWrongThreadStart"));
    }

    m_ThreadStartArg = parameter;
    StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
    Start(ref stackMark);
}

可以看到,参数的类型为 object 类型,下面就看看 start 方法中的这个参数的用途吧

using System;
using System.Threading;

namespace Test5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Thread thread = new Thread(Test);
            thread.Start(10);

            Console.ReadKey();
        }

        static void Test(object obj)
        {
            int len = (int)obj;
            for (int i = 0; i < len; i++)
            {
                Console.WriteLine(i);
                Thread.Sleep(500);
            }
        }
    }
}

运行:

将上面的方法换为委托,Lambda 表达式的写法,效果一样

using System;
using System.Threading;

namespace Test5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Thread thread = new Thread(delegate (object obj)
            {
                int len = (int)obj;
                for (int i = 0; i < len; i++)
                {
                    Console.WriteLine(i);
                    Thread.Sleep(500);
                }
            });
            thread.Start(10);

            Console.ReadKey();
        }
    }
}

 

十、线程中的 lock 关键字

lock 往往用在多线程的开发中,如果有几个线程,同时调用一个方法,就会出现这个方法轮流执行的情况,这可能会导致部分数据混乱,如果想这几个线程按顺序执行,则需要用到 lock。

下面是 lock 的一些注意点:

1.lock 只对多线程有效,对单线程无效,单线程 lock 不会导致死锁。
2.不推荐使用 lock(this),因为在它外部也可以访问它。
3.不应该使用 lock(string(类型)),因为 string 在内存分配上是重用的,可能会导致冲突。
4.lock 中包含的代码最好不要太多,因为在这里是单线程运行的。
5. .net 提供了一些线程安全的集合类,使用这些集合不需要用到 lock。
6.在可以使用数据分拆的方法来使用多线程时,最好使用数据分拆而不使用 lock。
7.lock 的对象应该是 private static readonly object Object_Lock = new object();
 

下面的代码,其实在上面的案例中用到了多次

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Test5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(Test);
            thread1.Name = "thread1";
            thread1.Start();
            Thread thread2 = new Thread(Test);
            thread2.Name = "thread2";
            thread2.Start();

            Console.ReadKey();
        }

        static int count = 0;

        static void Test()
        {
            while (count < 20)
            {
                count++;
                Console.WriteLine("当前线程:{0},次数:{1}", Thread.CurrentThread.Name, count);
                Thread.Sleep(50);
            }
        }
    }
}

运行:

从结果可以看到,方法是由这两个线程轮流执行的,比较混乱,如果线程执行的方法中有全局变量,那么有可能导致计算结果是不对的,下面,我们将代码更改一下

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Test5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(Test);
            thread1.Name = "thread1";
            thread1.Start();
            Thread thread2 = new Thread(Test);
            thread2.Name = "thread2";
            thread2.Start();

            Console.ReadKey();
        }

        private static readonly object obj = new object();

        private static int thread1Count = 0;
        private static int thread2Count = 0;

        static void Test()
        {
            lock (obj)
            {
                while (true)
                {
                    if (Thread.CurrentThread.Name == "thread1")
                        thread1Count++;
                    if (Thread.CurrentThread.Name == "thread2")
                        thread2Count++;

                    Console.WriteLine("线程:{0} thread1Count:{1}, thread2Count:{2}", Thread.CurrentThread.Name, thread1Count, thread2Count);

                    Thread.Sleep(50);

                    if (thread1Count > 5)
                    {
                        thread1Count = 0;
                        break;
                    }
                    if (thread2Count > 5)
                    {
                        thread2Count = 0;
                        break;
                    }
                }
            }
        }
    }
}

thread1Count = 0 是防止线程1执行完后,线程2执行时,依然满足条件,而直接退出了循环,所以,我将 thread1Count 设置为 0 了。

运行

从打印的结果来看,现在不再是两个线程轮流着执行了,这就是我们想要的效果了。

十一、Monitor类锁定

在上节中,我们使用了 lock 来锁定多线程的执行,实际上 lock 关键字是 Monitor 类用例的一个语法糖。如果我们分解使用了lock关键字的代码,将会看到它如下面代码所示:

object lockObject = new object();
bool acquiredLock = false;
try
{
    Monitor.Enter(lockObject, ref acquiredLock);
}
finally
{
    if (acquiredLock)
    {
        Monitor.Exit(lockObject);
    }
}

Monitor的常用属性和方法:

  • Enter(Object) 在指定对象上获取排他锁。
  • Exit(Object) 释放指定对象上的排他锁。
  • Pulse 通知等待队列中的线程锁定对象状态的更改。
  • PulseAll 通知所有的等待线程对象状态的更改。
  • TryEnter(Object) 试图获取指定对象的排他锁。
  • TryEnter(Object, Boolean) 尝试获取指定对象上的排他锁,并自动设置一个值,指示是否得到了该锁。
  • Wait(Object) 释放对象上的锁并阻止当前线程,直到它重新获取该锁。

案例:

using System;
using System.Threading;

namespace Test5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Thread threadA = new Thread(ThreadMethod);
            threadA.Name = "线程A";
            Thread threadB = new Thread(ThreadMethod);
            threadB.Name = "线程B";
            threadA.Start();
            threadB.Start();

            Console.ReadKey();
        }

        private static readonly object obj = new object();
        public static void ThreadMethod()
        {
            //锁定对象
            Monitor.Enter(obj);
            try
            {
                for (int i = 1; i <= 10; i++)
                {
                    Console.Write("{0}:{1}  ", Thread.CurrentThread.Name, i);
                }
                Console.WriteLine();
            }
            finally
            {
                //释放对象
                Monitor.Exit(obj); 
            }
        }
    }
}

运行:

 

结束

如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言,谢谢!

end

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

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

相关文章

让GPT-3、ChatGPT、GPT-4一起做脑筋急转弯,GPT-4一骑绝尘!

作者 | python 一个烙饼煎一面一分钟&#xff0c;两个烙饼煎两面几分钟&#xff1f; 让你来回答&#xff0c;是不是一不小心就掉到沟里了&#xff1f;如果让大语言模型来做这种脑筋急转弯会怎样呢&#xff1f;研究发现&#xff0c;模型越大&#xff0c;回答就越可能掉到沟里&a…

VScode连接远程服务器

VScode连接远程服务器 文章目录 VScode连接远程服务器下载扩展通过扩展连接服务器在输入框中输入usernameip进行连接通过已保存的配置信息进行连接 连接成功之后访问服务器文件访问文件 下载扩展 下载以下三个扩展 Remote-SSH Remote - SSH: Editing Configuration Files R…

Docker Network 基础

一、是什么 Docker网络是Docker容器之间和容器与外部网络之间的通信和连接的一种机制。在Docker中&#xff0c;每个容器都可以有自己的网络栈&#xff0c;包括网络接口、IP地址和网络配置。Docker网络提供了一种灵活且可定制的方式&#xff0c;使得容器之间可以相互通信&#x…

【单元测试】Junit 4(二)--eclipse配置Junit+Junit基础注解

目录 1.0 前言 1.1 配置Junit 4 1.1.1 安装包 1.1.2 创建Junit项目 1.2 Junit 4 注解 1.2.1 测试用例相关的注解 1.2.1.1 Before 1.2.1.2 After 1.2.1.3 BeforeClass 1.2.1.4 AfterClass 1.2.1.5 Test 1.2.1.6 Ignore 1.2.1.7 示例 1.2.2 打包测试Suite相关的注解…

JAVA工程打包

目录 一、工程代码和第三方依赖包分开 二、工程代码和第三方依赖包打入同一个jar包 1、工程的class文件和依赖的第三方jar包所包含的class文件打进同一个jar包中。部署时&#xff0c;直接部署该jar包即可。 2、如果是springboot工程&#xff0c;可以将工程的class文件和依赖…

iOS多语言解决方案全面指南

本文以及相关工具和代码旨在为已上线的iOS项目提供一种快速支持多语言的解决方案。由于文案显示是通过hook实现的&#xff0c;因此对App的性能有一定影响&#xff1b;除了特殊场景的文案显示需要手动支持外&#xff0c;其他任务均已实现自动化。 本文中的部分脚本代码基于 Chat…

OpenShift 4 - 可观测性之用 Network Observability Operator 对网络流量进行监控观测(视频)

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在支持 OpenShift 4.12 Loki Operator 5.7.2 Network observability 1.2.0 的环境中验证 文章目录 Network Observability 相关组件和架构安装 Network Observaility 功能安装 Operator配置对象存储配置 …

Scrapy的基本使用

目录 Scrapy是什么 安装 使用 获取更多页面信息 写入数据库 图片下载 文件下载 更改文件名称以及路径 更改图片名称以及路径 循环获取页面信息时&#xff0c;item的数据重复或者对不上 下载文件时获取文件流直接上传到某个地方 Scrapy是什么 Scrapy 是一个基于 Pyth…

园区自然人代开果真那么好?可以解决成本票缺失吗?

园区自然人代开果真那么好&#xff1f;可以解决成本票缺失吗&#xff1f; 《税筹顾问》专注于园区招商、企业税务筹划&#xff0c;合理合规助力企业节税&#xff01; 自然人代开也就是指个人跟公司发生业务往来的时候&#xff0c;公司要求个人开具发票&#xff0c;进行入账&am…

open-mmlab/mmocr 环境搭建、推理和训练入门教程【一】

文章目录 博文基础信息Linux 搭建 open-mmlab/mmocr 运行环境准备数据集准备必要的预训练模型推理训练测试可视化输出 &#x1f4d9; 预祝各位 前途似锦、可摘星辰 博文基础信息 https://mmocr.readthedocs.io/zh_CN/dev-1.x/get_started/quick_run.html显卡&#xff0c;11G 1…

【Pytorch】梯度裁剪——torch.nn.utils.clip_grad_norm_的原理及计算过程

文章目录 一、torch.nn.utils.clip_grad_norm_二、计算过程三、确定max_norm 众所周知&#xff0c;梯度裁剪是为了防止梯度爆炸。在训练FCOS算法时&#xff0c;因为训练过程出现了损失为NaN的情况&#xff0c;在github issue有很多都是这种训练过程出现loss为NaN&#xff0c;作…

RISCV Reader笔记_3 RISCV汇编

RISC-V 汇编语言 函数调用的步骤在计算机组成与设计中也有过涉及&#xff1a; 指定寄存器存入参数&#xff1b;跳转到函数开始位置&#xff08;jal&#xff09;&#xff1b;在callee中按需保存寄存器&#xff1b;执行函数&#xff1b;恢复保存的寄存器&#xff1b;把返回值存入…

使用传统图像处理算法+机器学习进行shadow detection

前言 阴影是图像中常见的现象&#xff0c;它们对于场景理解和分析非常重要。由于阴影区域通常比较暗淡&#xff0c;而且与周围物体区别较大&#xff0c;因此在图像处理和计算机视觉领域中&#xff0c;阴影检测是一个重要的研究方向。传统的阴影检测算法通常基于阈值或边缘检测…

深入理解 kernel panic 的流程

我们在项目开发过程中&#xff0c;很多时候会出现由于某种原因经常会导致手机系统死机重启的情况&#xff08;重启分Android重启跟kernel重启&#xff0c;而我们这里只讨论kernel重启也就是 kernel panic 的情况&#xff09;&#xff0c;死机重启基本算是影响最严重的系统问题了…

180_Power BI 新卡片图计算组与同环比应用

180_Power BI 新卡片图计算组与同环比应用 一、背景 在 2023 年 6 月&#xff0c;Power BI 更新了新的视觉对象&#xff1a;Card(new) 。 当前还需要在预览功能中将其打开。 我们在实际的应用中将新卡片图做了一些应用&#xff0c;先来看看具体效果。 Power BI 公共 web 效果…

安全区域内活动UWB标签,高精度UWB定位监测,室内厘米级测距应用

随着人们对于室内安全和定位需求的增加&#xff0c;相应的技术应运而生&#xff0c;超宽带&#xff08;UWB&#xff09;标签定位技术应用于室内定位领域&#xff0c;并获得了快速的发展和应用。 UWB技术是一种基于极窄脉冲的无线技术&#xff0c;它的主要特点是无载波&#xf…

软件测试技能,JMeter压力测试教程,setUp线程组批量登录(九)

前言 前面一篇已经实现了在 setUp 线程组实现单个用户先登录后提取token给其它线程组使用&#xff0c;在压测的时候&#xff0c;单个用户登录很显然不能满足我们的压测需求 我们在压测接口的时候&#xff0c;需批量获取多个用户登录后返回的token值&#xff0c;那么在setUp 线…

RabbitMQ消息队列高级特性

文章目录 1.消息的可靠投递2.ConSumer ACK消费者确认接收消息3.消费者限流4.TTL过期时间5.死信队列6.延迟队列7.日志与监控8.消息追踪 1.消息的可靠投递 在线上生产环境中&#xff0c;RabbitMQ可能会产生消息丢失或者是投递失败的一个场景&#xff0c;RabbitMQ为了避免这种场景…

Redis慢查询分析

慢查询分析 谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间&#xff0c;当超过预设阀值&#xff0c;就将这条命令的相关信息&#xff08;例如&#xff1a;发生时间&#xff0c;耗时&#xff0c;命令的详细信息&#xff09;记录下来。 执行一条命令分为如下4个部分…

【每日算法 数据结构(C++)】—— 05 | 判断单链表是否有环(解题思路、流程图、代码片段)

文章目录 01 | &#x1f451; 题目描述02 | &#x1f50b; 解题思路03 | &#x1f9e2; 代码片段 The future belongs to those who believe in the beauty of their dreams. 未来属于那些相信梦想之美的人 01 | &#x1f451; 题目描述 给你一个单链表&#xff0c;请判断其中是…