C#多线程入门概念及技巧

news2024/11/17 7:42:47

C#多线程入门概念及技巧

  • 一、什么是线程
    • 1.1线程的概念
    • 1.2为什么要多线程
    • 1.3线程池
    • 1.4线程安全
      • 1.4.1同步机制
      • 1.4.2原子操作
    • 1.5线程安全示例
      • 1.5.1示例一
      • 1.5.2示例二
    • 1.6C#一些自带的方法实现并行
      • 1.6.1 Parallel——For、ForEach、Invoke
      • 1.6.1 PLINQ——AsParallel、AsSequential、AsOrdered
    • 1.7Semaphore

一、什么是线程

1.1线程的概念

  1. 线程是操作系统中能够独立运行的最小单位,也是程序中能够并发执行的一段指令序列
  2. 线程是进程的一部分,一个进程可以包括多个线程,这个线程可以共享进程的资源
  3. 进程有入口线程,也可用创建更多的线程

1.2为什么要多线程

  1. 批量重复任务希望同时进行
  2. 多个不同任务希望同时进行,并且互不干扰

1.3线程池

  1. 一组预先创建的线程,可以被重复使用来执行多个任务
  2. 避免频繁地创建和销毁线程,从而减少了现成创建和销毁的开销,提高了系统的性能和效率
  3. 异步编程默认使用线程池

1.4线程安全

多个线程访问共享资源时,对共享资源的访问不会导致数据不一致或不可预期的结果

1.4.1同步机制

  1. 用于协调和控制多个线程之间的执行顺序和互斥访问共享资源
  2. 确保线程按照特定的顺序执行,避免竞态条件和数据不一致的问题

1.4.2原子操作

  1. 在执行过程中不会被中断的操作,不可分割,要么完全执行,要么完全不执行,没有中间状态
  2. 在多线程环境下,原子操作能够保证数据的一致性和可靠性,避免出现竞太条件和数据竞争的问题

1.5线程安全示例

1.5.1示例一

两个线程对一个变量进行操作,每个线程都让count增加10000,代码如下:

namespace ThreadStudy
{
    class Thread_Lock
    {
        const int total = 100_000; 
        public static int count = 0;

        static void Main(string[] args)
        {
            Thread thread1 = new Thread(new ThreadStart(ThreadMethod));
            Thread thread2 = new Thread(new ThreadStart(ThreadMethod));
            thread1.Start();
            thread2.Start();
            thread1.Join();
            thread2.Join();
            Console.WriteLine($"Count:{count}");
        }

        public static void ThreadMethod()
        {
            for (int i = 0; i < total; i++)
                 count++;
        }
    }
}

输出结果确不为两万,并且每次都不一样:

在这里插入图片描述
这是因为线程一在访问并修改这个变量值的时候,另一个线程也在访问并修改这个值,这就会导致一个线程修改后的值被另一个线程修改后的值给覆盖,这个时候我们就需要加锁,修改后的代码如下:

    class Thread_Lock
    {
        const int total = 100_000; 
        public static int count = 0;
        public static object lockobjcet = new object();
        
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(new ThreadStart(ThreadMethod));
            Thread thread2 = new Thread(new ThreadStart(ThreadMethod));
            thread1.Start();
            thread2.Start();
            thread1.Join();
            thread2.Join();
            Console.WriteLine($"Count:{count}");
        }

        public static void ThreadMethod()
        {
            for (int i = 0; i < total; i++)
            {
                lock (lockobjcet)
                count++;
                //这么写也可用 原子操作:
                //count++在底层可能经过了很多步才加一 这个过程中数据可能被其它线程更改
                //原子操作能一步完成,防止其它线程对变量进行更改
                //Interlocked.Increment(ref count);
            }
        }

    }

输出结果:
在这里插入图片描述

1.5.2示例二

正常结果是要输出0-19,不加锁的情况下就会输出一些无序数

public static Queue<int> queue = new Queue<int>();

        public static object lockObject = new object();

        static void Main(string[] args)
        {
            Thread producer = new Thread(new ThreadStart(AddNumber));
            Thread consumer1 = new Thread(new ThreadStart(WriteNumber));
            Thread consumer2 = new Thread(new ThreadStart(WriteNumber));
            producer.Start();
            consumer1.Start();
            consumer2.Start();
            producer.Join();
            consumer1.Interrupt();
            consumer2.Interrupt();
            consumer1.Join();
            consumer2.Join();
        }

        public static void AddNumber()
        {
            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(20);
                queue.Enqueue(i);
            }
        }

        public static void WriteNumber()
        {
            try
            {
                while (true)
                {
                    lock(lockObject)
                    if (queue.TryDequeue(out var res))
                    {
                        Console.WriteLine(res);
                        Thread.Sleep(1);
                    }
                }
            }
            catch (Exception)
            {
                Console.WriteLine("Thread interrupted");
            }
        }

输出结果:
在这里插入图片描述

1.6C#一些自带的方法实现并行

1.6.1 Parallel——For、ForEach、Invoke

正常For循环需要4s

class Program
    {
        static void Main(string[] args)
        {
            var sw = Stopwatch.StartNew();


            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(200);
                Console.WriteLine($"I:{i}");
            }

            Console.WriteLine($"Elapsed time: {sw.ElapsedMilliseconds}ms");
        }
    }

在这里插入图片描述
使用Parallel进行For循环:
效果提升近10倍,美滋滋

class Program
    {
        static void Main(string[] args)
        {
            var sw = Stopwatch.StartNew();
            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(200);
                Console.WriteLine($"I:{i}");
            }
            Console.WriteLine($"Elapsed time: {sw.ElapsedMilliseconds}ms");
        }
    }

在这里插入图片描述

1.6.1 PLINQ——AsParallel、AsSequential、AsOrdered

//ToDo 后续补充

1.7Semaphore

Semaphore可以控制线程开启的多少,比如Parallel.For开启了5个线程,而Semaphore定义只能开启三个,当有三个线程正在做时,那么其它的线程就不能够再做,Semaphore等待后要释放掉,最后面还需要Dispose,之前用Parallel在不控制线程的情况下需要400ms,现在控制线程数量,需要1400ms

        static void Main(string[] args)
        {
            var sw = Stopwatch.StartNew();
            //第一个参数 最开始有几个线程可以用 第二个参数 最多可以同时用几个线程
            var seamphore = new Semaphore(3, 3);

            Parallel.For(0, 20, i =>
            {
                seamphore.WaitOne();
                Thread.Sleep(200);
                Console.WriteLine($"I:{i}");
                seamphore.Release();
            });
            seamphore.Dispose();
            Console.WriteLine($"Elapsed time: {sw.ElapsedMilliseconds}ms");
        }

在这里插入图片描述

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

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

相关文章

TSINGSEE视频智能分析人员入侵AI检测算法如何让城市管理更加高效、智慧?

在城市管理场景中&#xff0c;经常面临着禁区垂钓、非法捕捞、行人闯红灯、小区盗窃、车辆乱停乱放等一系列管理难题&#xff0c;这给城市发展带来了不小的阻力&#xff0c;同时也极易增加管理的人力、物力和财力。传统的人员巡逻监管效率低并且存在时间差&#xff0c;很难及时…

2.4.0 Milky Way 强势登场!新功能大爆炸,让你High翻全场!

Yo开发达人们&#xff0c;我们有重磅新功能要给你们放送啦&#xff01; Check it out 数据汇总不再单调&#xff0c;新的聚合函数登场&#xff01; compact_state_agg #1359gauge_agg #1370first #1395last #1413mode #1440increase #1476delta #1395time_delta #1405rate #14…

内存映射:PS和PL DDR3的一些区别

之前写的一些资料&#xff1a; PS与PL互联与SCU以及PG082-CSDN博客 参考别人的资料&#xff1a; PL读写PS端DDR的设计_pl读写ps端ddr数据-CSDN博客 xilinx sdk、vitis查看地址_vitis如何查看microblazed地址_yang_wei_bk的博客-CSDN博客 可见&#xff0c;PS端的DDR3需要从…

从0到1实现一个前端监控系统(附源码)

目录 一、从0开始 二、上报数据方法 三、上报时机 四、性能数据收集上报 收集上报FP 收集上报FCP 收集上报LCP 收集上报DOMContentLoaded 收集上报onload数据 收集上报资源加载时间 收集上报接口请求时间 五、错误数据收集上报 收集上报资源加载错误 收集上报js错…

msvcp120.dll丢失的6种解决方法,教你如何修复dll文件丢失

“找不到msvcp120dll,无法继续执行代码的6个修复方案”。我相信很多朋友在运行某些程序时&#xff0c;可能会遇到这样的错误提示&#xff1a;“找不到msvcp120dll&#xff0c;无法继续执行代码”。那么&#xff0c;msvcp120dll究竟是什么&#xff1f;为什么会丢失呢&#xff1f…

Java基础知识第四讲:Java 基础 - 深入理解泛型机制

Java 基础 - 深入理解泛型机制 背景&#xff1a;Java泛型这个特性是从JDK 1.5才开始加入的&#xff0c;为了兼容之前的版本&#xff0c;Java泛型的实现采取了“伪泛型”的策略&#xff0c;即Java在语法上支持泛型&#xff0c;但是在编译阶段会进行所谓的“类型擦除”&#xff0…

NestJS——基于Node.js 服务器端应用程序的开发框架

文章目录 前言什么是 NestJS&#xff1f; 一、NestJS特性&#xff1f;二、使用步骤Typescript 知识后端开发基本知识新建项目目录结构 前言 Nestjs中文文档 什么是 NestJS&#xff1f; Nest (NestJS) 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的开发框架。它利用…

【JVM系列】- 寻觅·方法区的内容

寻觅方法区的内容 &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f31d;分享学习心得&#xff0c;欢迎指正&#xff0c;大家一起学习成长&#xff01; 文章目录 寻觅…

杂记 | 使用FRP搭建内网穿透服务(新版toml配置文件,搭配反向代理食用)

文章目录 01 需求与回顾02 下载程序包03 编辑.toml文件3.1 编辑frps.toml3.2 编辑frpc.toml 04 启动服务4.1 启动服务端4.2 启动客户端 05 配置反向代理&#xff08;可选&#xff09;06 windows设置为默认启动&#xff08;可选&#xff09;6.1 创建启动脚本6.2 设置为开机自启 …

【Spring Cloud】声明性REST客户端:Feign

Spring Cloud Feign ——fallback 服务降级 1. Feign 简介2. Feign 的基础使用2.1 普通 HTTP 请求2.2 Feign 远程调用上传文件接口 1. Feign 简介 Feign 是一个声明式的 HTTP 客户端&#xff0c;它简化了编写基于 REST 的服务间通信代码的过程。在 Spring Cloud 中&#xff0c…

你一定要学会的Java语法 -- 【继承】

书接上回&#xff0c;我们已经学完了类和对象&#xff0c;今天内容可能有一点难&#xff0c;相信自己能跨过这道坎。 目录 一. 继承 1.什么是继承 2. 继承的概念 3. 继承的语法 4.父类成员访问 子类和父类成员变量同名 子类和父类成员方法同名 5.super关键字 6.子类构…

POJ 3254 Corn Fields 状态压缩DP(铺砖问题)

一、题目大意 我们要在N * M的田地里种植玉米&#xff0c;有如下限制条件&#xff1a; 1、对已经种植了玉米的位置&#xff0c;它的四个相邻位置都无法继续种植玉米。 2、题目中有说一些块无论如何&#xff0c;都无法种植玉米。 求所有种植玉米的方案数&#xff08;不种植也…

Vector - CANoe - Vector Hardware Manager基础介绍

经常使用CANoe的人都知道&#xff0c;我们之前使用配置VN系列硬件通道的时候使用的是Vector Hardware Config&#xff0c;非常的方便&#xff0c;不过在Vector Driver Setup驱动版本大于22.14后&#xff0c;为了更好的适用车载以太网相关的配置&#xff0c;以及各个配置之间继承…

kubenetes-kubelet组件

一、kubelet架构 每个节点都运行一个kubelet进程&#xff0c;默认监听10250端口&#xff0c;kubelet作用非常重要&#xff0c;是节点的守护神。 接收并执行 master发来的指令。管理Pod及Pod中的容器。每个kubelet进程会在API Server 上注册节点自身信息&#xff0c;定期向mast…

Java学习_对象

对象在计算机中的执行原理 类和对象的一些注意事项 this关键字 构造器 构造器是一种特殊的方法 : 特殊之处在于&#xff0c;名字必须与所在类的名字一样&#xff0c;而且不能写返回值类型 封装 封装的设计规范&#xff1a;合理隐藏、合理暴露 实体类 成员变量和局部变量的区别 …

程序员的护城河:职业发展的关键元素

目录 1. 技术深度与广度 2. 项目经验与实际操作 3. 沟通与团队协作 4. 持续学习与自我更新 5. 社区参与与开源贡献 6. 创新思维与解决问题的能力 7. 职业规划与自我管理 结语 在科技日新月异的今天&#xff0c;程序员的竞争已经不再仅仅依赖于技术水平&#xff0c;而是…

Citespace的使用

CiteSpace CiteSpace的相关介绍运行CiteSpace CiteSpace的相关介绍 CiteSpace作为一款优秀的文献计量学软件&#xff0c;能够将文献之间的关系以科学知识图谱的方式可视化地展现在我们面前。简单来说&#xff0c;面对海量的文献&#xff0c;CiteSpace能够迅速锁定自己需要关注…

使用openvc进行人脸检测:Haar级联分类器

1 人脸检测介绍 1.1 什么是人脸检测 人脸检测的目标是确定图像或视频中是否存在人脸。如果存在多个面&#xff0c;则每个面都被一个边界框包围&#xff0c;因此我们知道这些面的位置 人脸检测算法的主要目标是准确有效地确定图像或视频中人脸的存在和位置。这些算法分析数据…

如何从零开始手写一个消息中间件(从宏观角度理解消息中间件的技术原理)

如何从零开始手写一个消息中间件&#xff08;从宏观角度理解消息中间件的技术原理&#xff09; 什么是消息中间件消息中间件的作用逐一拆解消息中间件的核心技术消息中间件核心技术总览IOBIONIOIO多路复用AIOIO多路复用详细分析selectpollepoll Java中的IO多路复用 协议序列化消…

阿里云严重故障,影响阿里系、淘宝、饿了么、语雀等都崩了...

作者&#xff1a;JavaPub 编程学习一条龙&#xff1a;http://luxian.javapub.net.cn 就在一年一度的双十一剁手节火热进行时&#xff0c;阿里云服务出现了严重故障。 关键是前不久刚发生了语雀事件&#xff0c;不了解的朋友阅读这里 阿里语雀突发P0级事故&#xff0c;一度崩溃…