【从零开始入门unity游戏开发之——C#篇37】进程、线程和C# 中实现多线程有多种方案

文章目录

  • 进程、线程和C#多线程
  • 一、`进程`的基本概念
  • 二、`线程`的基本概念
  • 三、C#中的多线程
    • 1、为什么需要多线程?
    • 2、*C# 中如何实现多线程**
      • 2.1 **使用 `Thread` 类**
        • (1)示例
        • (2)线程休眠
        • (3)设置为后台线程
          • 普通的前台线程
          • 设置为后台线程
        • (4)停止线程
      • 2.2 **使用 `Task` 类** (推荐)
      • 2.3 使用 ThreadPool 类
      • 2.4 **使用 `async` 和 `await`**
      • 2.5 使用 Parallel 类
    • 3、对比总结:
    • 4、锁(`lock`)线程安全
      • 4.1 问题分析
      • 4.2 解决方案
      • 4.3 示例:
      • 4.4 工作机制:
    • 5、死锁
      • 5.1 问题分析
      • 5.2 避免死锁的策略:
  • 四、总结
  • 专栏推荐
  • 完结

进程、线程和C#多线程

一、进程的基本概念

进程是计算机中正在运行的程序的实例。每个进程有自己的内存空间和资源,系统通过进程来隔离不同程序之间的执行。

例如,你打开一个浏览器,它会启动一个进程;然后,你打开一个文本编辑器,它又是另一个进程。每个进程相互独立,它们有自己的内存、文件句柄等。

我们打开win的任务管理器,这里其实就是一个个进程,一个正在运行的程序就是一个进程
在这里插入图片描述

二、线程的基本概念

线程是进程中的一个执行单元。一个进程至少有一个线程,这个线程负责执行进程中的代码。

如果你有一个程序,这个程序会启动一个主线程来执行代码。当程序需要执行多个任务时,可以在这个进程里创建多个线程。

线程是 CPU 调度的基本单位。你可以把线程理解成进程内部的工作小单元,多个线程可以在同一个进程中并行或交替执行。

三、C#中的多线程

C# 支持多线程编程,这意味着你可以同时执行多个任务或操作。举个简单的例子,你可以在一个程序里同时播放音乐、下载文件、处理数据,而这些操作并不会互相干扰。每个操作都可以由不同的线程处理。

1、为什么需要多线程?

假设你有一个程序需要做很多事情(比如下载文件、处理数据等),如果它只用一个线程,程序会在一个任务完成后才开始下一个任务,这会导致程序的响应变慢,特别是在处理耗时任务时。多线程的好处就是能并行或并发地处理这些任务,让程序更高效。

2、C# 中如何实现多线程*

在 C# 中实现多线程有多种方案,每种方案有不同的使用场景和特点。以下是几种常见的实现方式:

2.1 使用 Thread

Thread 类提供了直接的多线程控制,是最基本的多线程编程方式。

  • 优点:简单直接,易于理解。
  • 缺点:需要手动管理线程的生命周期,容易产生线程安全问题,效率较低。
(1)示例
using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // 创建一个线程,执行 DoWork 方法
        Thread t = new Thread(DoWork);
        t.Start(); // 启动线程

        // 主线程继续执行其他操作
        Console.WriteLine("Main thread is running.");
    }

    static void DoWork()
    {
        // 线程执行的任务
        Console.WriteLine("Working in the background...");
    }
}

在上面的例子中,我们创建了一个新的线程 t,并让它去执行 DoWork 方法。然后主线程会继续执行后面的代码。主线程和新线程是并行工作的。

(2)线程休眠

可以通过 Thread.Sleep() 方法让线程暂停执行指定的时间。

  • 参数是毫秒数,1秒=1000毫秒。

示例代码:

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread t = new Thread(NewThreadLogic);
        t.Start();
    }

    static void NewThreadLogic()
    {
        while (true)
        {
            Console.WriteLine("线程执行中...");
            Thread.Sleep(1000);  // 休眠1秒
        }
    }
}
(3)设置为后台线程

使用 Thread.IsBackground = true 将线程设置为后台线程,可以让你处理那些不需要程序等待完成的任务,提高程序的响应速度。

在 后台线程 的情况下,主线程结束时,后台线程会被强制终止,即使它还没有完成任务。

普通的前台线程
using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // 创建后台线程
        Thread t = new Thread(DoWork);
        t.Start();
        
        Console.WriteLine("主线程结束,后台线程继续执行中...");
    }

    static void DoWork()
    {
        int count = 0;
        while (count < 5)
        {
            Console.WriteLine($"工作线程执行中... {count}");
            Thread.Sleep(1000);  // 每秒执行一次
            count++;
        }
    }
}

结果,主线程结束时,如果有后台线程在运行,程序就不会退出,直到后台线程执行完毕。
在这里插入图片描述

设置为后台线程
using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // 创建后台线程
        Thread t = new Thread(DoWork);
        t.IsBackground = true;  // 设置为后台线程
        t.Start();

        // 主线程不等待后台线程
        Console.WriteLine("主线程结束,后台线程继续执行中...");
        // 主线程结束,程序直接退出
    }

    static void DoWork()
    {
        int count = 0;
        while (count < 5)
        {
            Console.WriteLine($"工作线程执行中... {count}");
            Thread.Sleep(1000);  // 每秒执行一次
            count++;
        }
    }
}

结果,在 后台线程 的情况下,主线程结束时,后台线程会被强制终止,即使它还没有完成任务。
在这里插入图片描述

(4)停止线程

对于死循环线程,必须通过某种方式来终止它。可以使用一个标志变量来控制线程停止。
也可以调用 Thread.Abort() 来强制终止线程,但该方法在 .NET Core 中已经被废弃,因为它会抛出 ThreadAbortException 异常,且有潜在的稳定性风险。所以这里就不介绍了。

使用标志变量停止线程

using System;
using System.Threading;

class Program
{
    static bool isRunning = true;

    static void Main()
    {
        Thread t = new Thread(NewThreadLogic);
        t.Start();

        // 稍后停止线程
        Thread.Sleep(5000);  // 等待5秒
        isRunning = false;   // 设置标志变量为false,线程会退出
    }

    static void NewThreadLogic()
    {
        while (isRunning)
        {
            Console.WriteLine("线程正在运行");
            Thread.Sleep(1000);  // 休眠1秒
        }
        Console.WriteLine("线程已停止");
    }
}

2.2 使用 Task (推荐)

除了 Thread 类,C# 还提供了 Task 类,它更高级、使用起来更简单,通常用于处理异步操作。Task 适合于执行一些后台工作,不需要创建管理线程。示例如下:

  • 优点:Task 自动管理线程池线程,提供了更高层次的抽象,支持任务组合、取消、异常处理等功能。
  • 缺点:由于是基于线程池,因此任务是异步执行的,可能不是实时响应的。
using System;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Task.Run(() => DoWork());
        Console.ReadLine(); // 防止程序过早退出
    }

    static void DoWork()
    {
        Console.WriteLine("Task is running...");
    }
}

2.3 使用 ThreadPool 类

ThreadPool 是 .NET 提供的一个线程池,可以重用线程池中的线程,从而减少线程的创建和销毁开销。

  • 优点:线程池可以重用线程,避免了线程的频繁创建和销毁,效率较高。
  • 缺点:线程池的线程是共享的,可能会引发竞态条件(Race Condition),需要特别注意线程安全。
using System;
using System.Threading;

class Program
{
    static void Main()
    {
        ThreadPool.QueueUserWorkItem(DoWork);
    }

    static void DoWork(object state)
    {
        Console.WriteLine("ThreadPool thread is running...");
    }
}

2.4 使用 asyncawait

C# 中 async 和 await 关键字使得异步编程变得更加简单,虽然它并不直接涉及多线程,但可以实现非阻塞的操作,常用于 I/O 密集型任务(如网络请求、文件操作等)。

  • 优点:代码结构清晰,不需要显式创建线程,可以轻松处理异步操作。
  • 缺点:适用于 I/O 密集型操作,对于 CPU 密集型任务效果不佳。
using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        // 异步执行方法
        await DoWorkAsync();

        Console.WriteLine("Main thread is running.");
    }

    static async Task DoWorkAsync()
    {
        // 模拟耗时任务
        await Task.Delay(2000); // 延迟2秒
        Console.WriteLine("Work completed in the background.");
    }
}

2.5 使用 Parallel 类

Parallel 类是并行编程的高级 API,通常用于对大量数据进行并行处理。它可以自动分配任务到多个线程,适用于大规模数据处理等场景。

  • 优点:简化并行任务的实现,自动管理线程和任务分配。
  • 缺点:适用于需要大量并行计算的场景,对于简单的任务可能显得过于复杂。
using System;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Parallel.For(0, 10, i =>
        {
            Console.WriteLine($"Task {i} is running...");
        });
    }
}

3、对比总结:

方案描述优点缺点实际应用示例
Thread使用 System.Threading.Thread 类直接创建和管理线程。控制灵活,可以创建、启动和管理线程;可以访问线程状态等。线程创建和销毁开销较大;较低级别,需要手动管理线程生命周期。执行长时间运算或独立任务,如后台下载或复杂计算任务。
Task使用 System.Threading.Tasks.Task 类进行任务管理,支持异步编程。更高层次的抽象;自动管理线程池;支持异步编程;异常处理更简洁。相较于 Thread,无法精细控制线程;适合短时间任务。网络请求、文件操作等 I/O 密集型操作;并行计算等。
ThreadPool使用 ThreadPool 提供的线程池进行线程管理。重用线程,减少线程创建销毁的开销;自动管理线程;适合短时间任务。不能精确控制线程的生命周期;不适合需要大量 CPU 时间的任务。高并发任务、简单的后台操作、事件处理等。
async/await基于异步编程模型,使用 asyncawait 关键字进行异步调用。简化异步编程;不会阻塞线程;适合 I/O 密集型操作。只能用于 I/O 密集型任务;不适用于 CPU 密集型任务。异步文件读取、数据库访问、Web API 请求等 I/O 密集型操作。
Parallel使用 System.Threading.Tasks.Parallel 类来执行并行操作。简化并行计算;自动分配任务到线程池中的线程;易于实现并行。无法控制线程的数量和执行方式;仅适合 CPU 密集型任务。大规模数据处理(例如数组排序、并行计算等)。

4、锁(lock)线程安全

4.1 问题分析

多个线程同时访问共享数据时,可能会导致数据冲突或者不一致。(比如线程1想修改a变量,线程2又想获取a变量。)

4.2 解决方案

因此,在多线程编程中,要特别注意线程安全。常用的做法是使用锁(lock)来避免同时访问共享资源。

4.3 示例:

using System;
using System.Threading;

class Program
{
    static object obj = new object();  // 用于加锁的对象

    static void Main()
    {
        Thread t1 = new Thread(ThreadLogic);
        Thread t2 = new Thread(ThreadLogic);
        
        t1.Start();
        t2.Start();
    }

    static void ThreadLogic()
    {
        while (true)
        {
            lock (obj)  // 锁定共享资源
            {
                //操作共享数据
            }
            Thread.Sleep(1000);  // 休眠1秒
        }
    }
}

lock 锁定的对象通常是一个引用类型(例如 object)。通常情况下,最好选择一个私有的、专用的对象作为锁对象,而不是使用诸如 this 或者 typeof(MyClass) 这样的公共对象。这样做的原因是防止外部代码错误地修改锁对象,导致不安全的同步行为。

4.4 工作机制:

  • 进入锁:当线程执行 lock (obj) 时,它会尝试获得 obj的锁。

    • 如果没有其他线程持有该锁,当前线程就能进入锁定的代码块。
    • 如果其他线程已经持有该锁,当前线程将被阻塞,直到锁被释放。
  • 释放锁:当线程执行完 lock 代码块中的代码后,自动释放锁,使得其他等待的线程可以获得锁并进入临界区。

5、死锁

5.1 问题分析

如果程序中存在多个线程互相等待对方释放锁的情况,可能会发生死锁。比如线程 A 持有锁 1,等待锁 2,线程 B 持有锁 2,等待锁 1,这种情况会导致两个线程永远无法继续执行。

5.2 避免死锁的策略:

  • 尽量避免多个锁的嵌套。
  • 遵循一致的锁定顺序:如果多个线程需要获取多个锁,应该遵循相同的顺序获取锁,以防止死锁。

四、总结

  1. 进程是一个正在运行的程序,它有自己的内存和资源。
  2. 线程是进程中的执行单位,一个进程至少有一个线程,多个线程可以在同一个进程中并行工作。
  3. 在 C# 中,多线程可以通过 Thread 类、Task 类以及 async/await 来实现。
  4. 需要注意线程安全问题,避免并发访问导致数据冲突。

专栏推荐

地址
【从零开始入门unity游戏开发之——C#篇】
【从零开始入门unity游戏开发之——unity篇】
【制作100个Unity游戏】
【推荐100个unity插件】
【实现100个unity特效】
【unity框架开发】

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

评分模型在路网通勤习惯分析中的应用——提出问题(1)

1、问题的由来、目标和意义 最近一段时间和公司其它业务部门讨论时&#xff0c;发现一个有趣的交通路网问题&#xff0c;车辆从S点行驶到V点共用时40分钟&#xff0c;这段时间内路网中的卡口摄像头识别到了车辆通过的信息。如下图所示&#xff1a; 设计师需要通过这些有限的路…

机器学习DAY7: 特征工程和特征选择(数据预处理)(完)

本文通过特征提取、特征转换、特征选择三个过程介绍数据预处理方法&#xff0c;特征提取将原始数据转换为适合建模的特征&#xff0c;特征转换将数据进行变换以提高算法的准确性&#xff0c;特征选择用来删除无用的特征。 知识点 特征提取特征转换特征选择 本次实验的一些示…

【Unity3D】Jobs、Burst并行计算裁剪Texture3D物体

版本&#xff1a;Unity2019.4.0f1 PackageManager下载Burst插件(1.2.3版本) 利用如下代码&#xff0c;生成一个Texture3D资源&#xff0c;它只能脚本生成&#xff0c;是一个32*32*32的立方体&#xff0c;导出路径记得改下&#xff0c;不然报错。 using UnityEditor; using Uni…

紫光同创-盘古200pro+开发板

本原创文章由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处&#xff08;www.meyesemi.com) 一、开发系统介绍 开发系统概述 MES2L676-200HP 开发板采用紫光同创 logos2 系列 FPGA&#xff0c;型号&#xff1a;…

【后端】LNMP环境搭建

长期更新各种好文&#xff0c;建议关注收藏&#xff01; 本文近期更新完毕。 LNMPlinuxnginxmysqlphp 需要的资源 linux服务器 web服务软件nginx 对应的语言编译器代码文件 数据库mysql安装 tar.gz包或者命令行安装 进入root&#xff1a; sodu 或su mkdir path/{server,soft}…

VSCode设置Playwright教程

1.安装扩展 打开VS Code&#xff0c;在扩展—>搜索"Playwright Test for VSCode"&#xff0c;点击安装 按快捷键CommandShiftP&#xff0c;输入install playwright&#xff0c;点击安装Playwright 安装成功会有如下提示 2.调试脚本 打开tests/example.spec.ts文…

RK3566和Robo_C的EMC防护设计细节

USB部分的防护细节&#xff1a; ROBO C的USB接口&#xff1a; PF级别的电容滤波&#xff1a; TVS电容&#xff08;TVS Capacitor&#xff09;&#xff1a;用于与TVS二极管配合&#xff0c;保护电路免受瞬态电压冲击。电容一般较小&#xff0c;通常为几十皮法&#xff08;pF&am…

MicroDiffusion——采用新的掩码方法和改进的 Transformer 架构,实现了低预算的扩散模型

介绍 论文地址&#xff1a;https://arxiv.org/abs/2407.15811 现代图像生成模型擅长创建自然、高质量的内容&#xff0c;每年生成的图像超过十亿幅。然而&#xff0c;从头开始训练这些模型极其昂贵和耗时。文本到图像&#xff08;T2I&#xff09;扩散模型降低了部分计算成本&a…

使用 Three.js 创建一个 3D 人形机器人仿真系统

引言 在这篇文章中&#xff0c;我们将探讨如何使用 Three.js 创建一个简单但有趣的 3D 人形机器人仿真系统。这个机器人可以通过键盘控制进行行走和转向&#xff0c;并具有基本的动画效果。 技术栈 HTML5Three.jsJavaScript 实现步骤 1. 基础设置 首先&#xff0c;我们需要…

【c++高阶DS】最小生成树

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 目录 01.最小生成树Kruskal算法Prim算法 01.最小生成树 连通图中的每一棵生成树&#xff0c;都是原图的一个极大无环子图&#xff0c;即&#xff1a;从其中删去任何一条边&#xff0c;生成…

自学记录鸿蒙API 13:实现人脸比对Core Vision Face Comparator

完成了文本识别和人脸检测的项目后&#xff0c;我发现人脸比对是一个更有趣的一个小技术玩意儿。我决定整一整&#xff0c;也就是对HarmonyOS Next最新版本API 13中的Core Vision Face Comparator API的学习&#xff0c;这项技术能够对人脸进行高精度比对&#xff0c;并给出相似…

2024/12/29 黄冈师范学院计算机学院网络工程《路由期末复习作业一》

一、选择题 1.某公司为其一些远程小站点预留了网段 172.29.100.0/26&#xff0c;每一个站点有10个IP设备接到网络&#xff0c;下面那个VLSM掩码能够为该需求提供最小数量的主机数目 &#xff08; &#xff09; A./27 B./28 C./29 D./30 -首先审题我们需要搞清楚站点与网…

redis cluster集群

华子目录 什么是redis集群redis cluster的体系架构什么是数据sharding&#xff1f;什么是hash tag集群中删除或新增节点&#xff0c;数据如何迁移&#xff1f;redis集群如何使用gossip通信?定义meet信息ping消息pong消息fail消息&#xff08;不是用gossip协议实现的&#xff0…

PrimeVue菜单模块(Menu),看api的重要性

以下是对PrimeVue菜单模块&#xff08;Menu&#xff09;的API属性的中文详解&#xff1a; 一、整体概述 PrimeVue的菜单&#xff08;Menu&#xff09;是一个支持动态和静态定位的导航/命令组件&#xff0c;其API通过定义一些辅助的属性&#xff08;props&#xff09;、事件等&…

STM32中断详解

STM32中断详解 NVIC 中断系统中断向量表相关寄存器中断优先级中断配置 外部中断实验EXTI框图外部中断/事件线映射中断步骤初始化代码实现 定时器中断通用定时器相关功能标号1&#xff1a;时钟源标号 2&#xff1a;控制器标号 3&#xff1a;时基单元 代码实现 NVIC 中断系统 STM…

从零开始开发纯血鸿蒙应用之逻辑封装

从零开始开发纯血鸿蒙应用 一、前言二、逻辑封装的原则三、实现 FileUtil1、统一的存放位置2、文件的增删改查2.1、文件创建与文件保存2.2、文件读取2.2.1、读取内部文件2.2.2、读取外部文件 3、文件删除 四、总结 一、前言 应用的动态&#xff0c;借助 UI 响应完成&#xff0…

《机器学习》——线性回归模型

文章目录 线性回归模型简介一元线性回归模型多元线性回归模型误差项分析一元线性模型实例完整代码 多元线性模型实例完整代码 线性回归模型简介 线性回归是利用数理统计中回归分析&#xff0c;来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法。 相关关系&…

【深度学习环境】NVIDIA Driver、Cuda和Pytorch(centos9机器,要用到显示器)

文章目录 一 、Anaconda install二、 NIVIDIA driver install三、 Cuda install四、Pytorch install 一 、Anaconda install Step 1 Go to the official website: https://www.anaconda.com/download Input your email and submit. Step 2 Select your version, and click i…

在HTML中使用Vue如何使用嵌套循环把集合中的对象集合中的对象元素取出来(我的意思是集合中还有一个集合那种)

在 Vue.js 中处理嵌套集合&#xff08;即集合中的对象包含另一个集合&#xff09;时&#xff0c;使用多重 v-for 指令来遍历这些层次结构。每个 v-for 指令可以用于迭代一个特定级别的数据集&#xff0c;并且可以在模板中嵌套多个 v-for 来访问更深层次的数据。 例如&#xff…

ip归属地是什么意思?ip归属地是实时定位吗

在数字化时代&#xff0c;IP地址作为网络设备的唯一标识符&#xff0c;不仅关乎设备间的通信&#xff0c;还涉及到用户的网络身份与位置信息。其中&#xff0c;IP归属地作为IP地址的地理位置信息&#xff0c;备受用户关注。本文将详细解析IP归属地的含义&#xff0c;并探讨其是…