当前位置: 首页 > news >正文

c# 多线程

课程

bilibili Threading in C#

XiaoLi.Notes: 学习笔记

图解系统介绍 | 小林coding | Java面试学习

C# 中的多线程 - GKarch

一 概念

1.


为什么出现先打印主线程再打印新线程的情况吗?线程顺序是怎样的?包含主线程和非主线程的区分和线程执行安排(时间,资源,冲突等等)

一、主线程与非主线程的区分

  1. 主线程(Main Thread)

    • 是 Unity 程序的 “主执行线程”,负责处理生命周期函数(StartUpdate等)、渲染、UI 交互、物理计算等核心逻辑。

    • 你的代码中,Start方法和直接调用的ThreadMethod1(2)都运行在主线程。

  2. 非主线程(子线程)

    • 通过new Thread(...)创建的是独立子线程,与主线程并行执行,不能直接操作 Unity 引擎 API(如Debug.Log实际会被 Unity 转发到主线程处理,但本身线程是独立的)。

    • 代码中new Thread(()=>ThreadMethod1(1)).Start()启动的是子线程。

二、线程执行顺序的本质:并发与调度

线程的执行顺序不固定,由操作系统的线程调度器决定,核心原因:

  1. 线程启动有开销:创建子线程需要分配资源、初始化上下文,这个过程需要时间。而主线程中ThreadMethod1(2)是直接调用,几乎没有延迟,因此大概率先执行。

  2. 调度器的不确定性:操作系统调度器根据线程优先级、CPU 核心负载等动态分配执行时间片(毫秒级)。即使子线程启动很快,也可能因为调度器先给主线程分配时间片而晚执行。

    例如:

    • 可能先打印arg=2(主线程),再打印arg=1(子线程);

    • 极端情况下(如子线程启动极快且调度器优先调度),也可能先打印arg=1

三、线程的时间、资源与冲突问题

  1. 时间安排

    • 线程是 “并发执行” 的,即宏观上同时运行,微观上 CPU 通过时间片轮转切换执行(单核)或多核并行(多核 CPU)。

    • 主线程和子线程的执行进度完全独立,没有固定的先后顺序,除非通过同步机制(如lockManualResetEvent)强制控制。

  2. 资源竞争

    • 若多个线程(包括主线程和子线程)访问共享资源(如全局变量、类成员变量),可能导致数据不一致。例如:

      private int count = 0; public void ThreadMethod1(int arg) { for (int i = 0; i < 1000; i++) { count++; // 多线程同时修改count,可能导致结果错误 } }
    • 解决方式:用lock锁定共享资源,保证同一时间只有一个线程访问:

      private object lockObj = new object(); public void ThreadMethod1(int arg) { lock (lockObj) { // 加锁,避免资源竞争 for (int i = 0; i < 1000; i++) { count++; } } }
  3. Unity 中的线程限制

    • 子线程不能直接调用 Unity 引擎 API(如Instantiatetransform.positionDebug.Log虽然能运行但内部会被转发,不建议在子线程频繁使用)。

    • 若子线程需要修改游戏对象状态,需通过 “主线程委托队列” 间接执行(例如用UnityWebRequest的回调、或自定义Update中执行的队列)。

函数和线程有啥区别

定义与概念

  • 函数:是一段具有特定功能、可以被重复调用的代码块。它是为了将复杂的程序逻辑进行模块化拆分,方便代码的复用、维护和阅读。在编程语言中,通过函数名和参数列表来调用函数,函数执行完成后将结果返回给调用者 。例如在 C 语言中:

int add(int a, int b) { return a + b; } int main() { int result = add(3, 5); return 0; }

这里add函数实现了两个整数相加的功能,在main函数中调用add函数并获取计算结果。

  • 线程:是进程中的一个执行单元,是程序执行的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源,比如内存空间、文件句柄等。线程可以并发或并行执行,从而提高程序的执行效率。例如在 Java 中创建一个简单的线程:

public class MyThread extends Thread { @Override public void run() { System.out.println("线程正在执行"); } public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } }

通过创建Thread类的子类并重写run方法,定义线程执行的逻辑,然后调用start方法启动线程。

执行方式

  • 函数:函数是顺序执行的,当在一个函数中调用另一个函数时,程序会暂停当前函数的执行,转而执行被调用的函数,直到被调用函数执行完毕并返回结果,才会继续执行当前函数后续的代码。例如:

def func1(): print("func1开始执行") func2() print("func1继续执行") def func2(): print("func2执行") func1()

输出结果是func1开始执行->func2执行->func1继续执行,体现了函数的顺序执行特性。

  • 线程:线程是并发或并行执行的。多个线程可以同时运行(在多核处理器上实现并行,在单核处理器上通过时间片轮转实现并发)。例如在多线程程序中,一个线程在进行网络请求等待响应时,其他线程可以继续执行其他任务,而不需要等待该线程完成网络请求,从而提高了程序整体的执行效率。

资源占用

  • 函数:函数本身不单独占用系统资源(除了运行时占用的栈空间等少量资源),它在调用时使用调用者所在的上下文环境,比如在一个函数中访问的变量,默认是调用该函数的作用域中的变量 。

  • 线程:线程需要占用一定的系统资源,包括线程栈(用于存储局部变量、函数调用信息等)、程序计数器(记录线程执行的位置)等。多个线程共享所属进程的内存空间等资源,这也带来了资源竞争和同步的问题,需要额外的机制(如锁)来处理。

生命周期

  • 函数:函数的生命周期从被调用开始,到执行完毕返回结果结束。每次调用函数时,都会创建新的栈帧(包含局部变量等信息),函数返回后栈帧被销毁。

  • 线程:线程的生命周期相对复杂,包括创建、就绪、运行、阻塞、终止等状态。线程可以通过特定的方法(如 Java 中的start方法)启动,在执行过程中可能因为等待资源等原因进入阻塞状态,当满足条件后又回到就绪或运行状态,最后执行完毕或被终止。

相互关系

函数可以作为线程执行的代码载体,通常在线程启动时,会指定一个函数(在 Java 中是run方法 ,在 C++ 中可以是自定义的函数对象等)作为线程执行的逻辑。例如在 C++ 中使用标准库创建线程:

#include <iostream> #include <thread> void threadFunction() { std::cout << "线程正在执行" << std::endl; } int main() { std::thread myThread(threadFunction); myThread.join(); return 0; }

2. CLR

CLR(Common Language Runtime,公共语言运行时)是微软 .NET Framework 的核心组件,它提供了一个托管代码执行环境,负责管理 .NET 应用程序的运行。你可以把它理解为 .NET 程序的“操作系统”或“虚拟机”。

功能类别

核心作用

具体表现或优势

​代码管理​

负责 .NET 代码的整个执行过程

将源代码编译为中间语言(IL),运行时通过即时编译(JIT)生成特定平台的本地机器码并执行

​内存管理​

自动处理内存的分配和释放

通过垃圾回收器(GC)自动回收不再使用的对象内存,有效避免内存泄漏和悬挂指针等问题

​类型安全​

确保代码的类型安全

通过公共类型系统(CTS)定义标准数据类型,并在加载和执行代码时进行类型安全检查,保证不同语言编写的对象可以正确交互

​安全性​

提供代码执行的安全环境

通过代码访问安全性(CAS)等技术控制代码的执行权限,根据代码来源和签名等方式确保安全

​跨语言集成​

支持多种编程语言在 .NET 平台上的集成和互操作

使不同语言(如 C#、VB.NET、F#)编写的组件可以无缝通信和协作

​线程与异常管理​

提供统一的异常处理模型和支持多线程编程

自动处理线程同步和资源竞争问题,并提供try-catch-finally等结构化异常处理机制

工作原理

CLR 的工作流程可以概括为以下几个关键步骤

  1. ​编译​​:你用 C#、VB.NET 等语言编写的源代码,首先会被编译器编译成​​中间语言(IL)​​ 和​​元数据(Metadata)​​,并打包成​​程序集​​(.dll 或 .exe 文件)。

  2. ​加载​​:当运行程序时,CLR 会将这些程序集加载到内存中。

  3. ​验证​​:CLR 会对 IL 代码进行验证,确保其类型安全且符合规范。

  4. ​JIT 编译​​:当需要执行某段代码(如方法)时,CLR 的​​即时编译器(JIT Compiler)​​ 会将其编译成本地机器的原生代码。

  5. ​执行​​:编译后的原生代码被 CPU 执行,在此过程中,CLR 会持续管理内存、安全、异常处理等。

  6. ​垃圾回收​​:CLR 的垃圾回收器会周期性地自动回收不再使用的内存。

二 原理

Unity线程

一 UniTask

1. 官方说明文档

GitHub - Cysharp/UniTask: Provides an efficient allocation free async/await integration for Unity.

// extension awaiter/methods can be used by this namespace using Cysharp.Threading.Tasks; // You can return type as struct UniTask<T>(or UniTask), it is unity specialized lightweight alternative of Task<T> // zero allocation and fast excution for zero overhead async/await integrate with Unity async UniTask<string> DemoAsync() { // You can await Unity's AsyncObject var asset = await Resources.LoadAsync<TextAsset>("foo"); var txt = (await UnityWebRequest.Get("https://...").SendWebRequest()).downloadHandler.text; await SceneManager.LoadSceneAsync("scene2"); // .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject // after Unity 2022.2, you can use `destroyCancellationToken` in MonoBehaviour var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy()); // .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T> var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x))); // await frame-based operation like a coroutine await UniTask.DelayFrame(100); // replacement of yield return new WaitForSeconds/WaitForSecondsRealtime await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false); // yield any playerloop timing(PreUpdate, Update, LateUpdate, etc...) await UniTask.Yield(PlayerLoopTiming.PreLateUpdate); // replacement of yield return null await UniTask.Yield(); await UniTask.NextFrame(); // replacement of WaitForEndOfFrame #if UNITY_2023_1_OR_NEWER await UniTask.WaitForEndOfFrame(); #else // requires MonoBehaviour(CoroutineRunner)) await UniTask.WaitForEndOfFrame(this); // this is MonoBehaviour #endif // replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate)) await UniTask.WaitForFixedUpdate(); // replacement of yield return WaitUntil await UniTask.WaitUntil(() => isActive == false); // special helper of WaitUntil await UniTask.WaitUntilValueChanged(this, x => x.isActive); // You can await IEnumerator coroutines await FooCoroutineEnumerator(); // You can await a standard task await Task.Run(() => 100); // Multithreading, run on ThreadPool under this code await UniTask.SwitchToThreadPool(); /* work on ThreadPool */ // return to MainThread(same as `ObserveOnMainThread` in UniRx) await UniTask.SwitchToMainThread(); // get async webrequest async UniTask<string> GetTextAsync(UnityWebRequest req) { var op = await req.SendWebRequest(); return op.downloadHandler.text; } var task1 = GetTextAsync(UnityWebRequest.Get("http://google.com")); var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com")); var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com")); // concurrent async-wait and get results easily by tuple syntax var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3); // shorthand of WhenAll, tuple can await directly var (google2, bing2, yahoo2) = await (task1, task2, task3); // return async-value.(or you can use `UniTask`(no result), `UniTaskVoid`(fire and forget)). return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found"); }

2. 方法

2. 1 基础概念与优势

  • async UniTask<string>:UniTask 是 struct 类型(值类型),相比Task更轻量,几乎无内存分配,适合 Unity 性能敏感场景。返回值支持泛型(这里是string),提供类型安全。

  • 核心目标:替代 Unity 传统协程(IEnumerator)和 .NETTask,实现零开销的异步编程。

2. 与 Unity 原生异步对象的集成

// 等待资源加载完成 var asset = await Resources.LoadAsync<TextAsset>("foo"); // 等待网络请求完成 var txt = (await UnityWebRequest.Get("https://...").SendWebRequest()).downloadHandler.text; // 等待场景加载完成 await SceneManager.LoadSceneAsync("scene2");
  • UniTask 可以直接awaitUnity 原生的异步操作(如Resources.LoadAsyncUnityWebRequestSceneManager.LoadSceneAsync),无需手动封装协程。

  • 相比传统协程的yield return,代码更线性,可读性更高。

3. 生命周期绑定与取消操作

// 将异步操作与当前组件生命周期绑定,组件销毁时自动取消任务 var asset2 = await Resources.LoadAsync<TextAsset>("bar") .WithCancellation(this.GetCancellationTokenOnDestroy());
  • GetCancellationTokenOnDestroy():获取与当前MonoBehaviour绑定的取消令牌,当组件被销毁时,自动取消异步操作,避免空引用错误。

  • WithCancellation():为异步操作添加取消支持,增强代码安全性。

4. 进度回调

// 监听异步操作的进度(如资源加载进度) var asset3 = await Resources.LoadAsync<TextAsset>("baz") .ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
  • ToUniTask():将 Unity 原生异步操作转换为 UniTask,并支持进度回调。

  • Progress.Create<float>():轻量级进度接口,替代 .NET 原生的IProgress<T>,减少内存分配。

5. 帧循环与时间相关的等待

// 等待指定帧数(替代 yield return new WaitForEndOfFrame + 计数) await UniTask.DelayFrame(100); // 等待指定时间(替代 WaitForSeconds,支持忽略时间缩放) await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false); // 在指定的 PlayerLoop 时机继续(如 PreLateUpdate、FixedUpdate 等) await UniTask.Yield(PlayerLoopTiming.PreLateUpdate); // 等待下一帧(替代 yield return null) await UniTask.Yield(); await UniTask.NextFrame(); // 等待帧结束(替代 WaitForEndOfFrame) await UniTask.WaitForEndOfFrame(this); // 需要传入 MonoBehaviour(2023 前版本) // 等待 FixedUpdate 阶段(替代 WaitForFixedUpdate) await UniTask.WaitForFixedUpdate();
  • 这些方法完美适配 Unity 的帧循环和时间系统,比传统协程的yield return语法更直观。

  • 支持细粒度控制代码执行时机(如在PreLateUpdate还是FixedUpdate阶段继续)。

6. 条件等待

// 等待条件满足(替代 WaitUntil) await UniTask.WaitUntil(() => isActive == false); // 等待值变化(简化版 WaitUntil) await UniTask.WaitUntilValueChanged(this, x => x.isActive);
  • WaitUntil:等待 lambda 表达式返回true时继续。

  • WaitUntilValueChanged:监听对象的某个属性(如isActive),当值变化时继续,简化条件判断代码。

7. 与其他异步类型的兼容性

// 等待传统协程(IEnumerator) await FooCoroutineEnumerator(); // 等待 .NET 原生 Task(如多线程任务) await Task.Run(() => 100);
  • UniTask 可与 Unity 传统协程、.NETTask无缝互操作,无需担心兼容性问题。

8. 线程切换

// 切换到线程池(后台线程)执行耗时操作 await UniTask.SwitchToThreadPool(); /* 这里的代码在后台线程执行(不能操作 Unity 主线程对象) */ // 切回主线程(操作 UI 或 GameObject) await UniTask.SwitchToMainThread();
  • 方便地在主线程和后台线程间切换,解决 Unity 中 “主线程才能操作游戏对象” 的限制。

  • 适合将密集计算(如数据解析)放到后台线程,避免卡顿。

9. 并行任务与结果聚合

// 并发执行多个网络请求 var task1 = GetTextAsync(UnityWebRequest.Get("http://google.com")); var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com")); var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com")); // 等待所有任务完成,并通过元组获取结果(简洁语法) var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3); // 更简洁的写法:元组直接 await var (google2, bing2, yahoo2) = await (task1, task2, task3);
  • 并行执行:多个异步任务同时进行,提升效率(如同时加载多个资源或发起多个网络请求)。

  • 结果聚合:通过元组语法一次性获取所有任务的结果,比传统Task.WhenAll更直观。

总结

UniTask 的核心能力:

  • 零 GC 开销:适合移动设备等性能敏感场景。

  • 深度适配 Unity:完美结合 Unity 生命周期、帧循环和异步操作。

  • 简洁语法:通过async/await替代繁琐的协程yield return

  • 强大兼容性:与传统协程、Task无缝互操作。

二 Task

https://blog.csdn.net/m0_53442125/article/details/140565129?fromshare=blogdetail&sharetype=blogdetail&sharerId=140565129&sharerefer=PC&sharesource=m0_73740524&sharefrom=from_link

http://www.cnnetsun.cn/news/1316720.html

相关文章:

  • Android性能优化终极指南:Sunflower中的ViewModel与数据预加载实践
  • 终极指南:imgaug 0.4.0重大更新与批量处理引擎深度剖析
  • Realm数据库版本控制终极指南:如何无缝处理应用升级的数据变更
  • Sorcar与传统建模对比:为什么250+节点能让你告别重复劳动?
  • 终极指南:EfficientDet核心组件SeparableConvBlock实现原理与实战应用
  • Skyplane未来路线图:即将发布的5大功能让跨云传输更智能
  • 终极指南:使用 SVG.js 创建完美响应式 SVG 图形的最佳方法
  • Qwen3-ASR-1.7B保姆级教程:Windows WSL2 + NVIDIA驱动环境下完整部署流程
  • Stable Yogi Leather-Dress-Collection开源大模型应用:高校动漫专业AI绘图实验课教案设计
  • HTTPDump完全指南:高效网络流量分析与API调试利器
  • RMBG-2.0开源大模型部署:兼容国产昇腾910B,ACL推理性能实测报告
  • Qwen3-ForcedAligner-0.6B开源可部署:完全离线运行保障语音数据零泄露
  • ollama部署Phi-4-mini-reasoning:轻量模型在嵌入式AI场景的应用探索
  • PYNQ项目极速安装指南:3步开启嵌入式Python开发新时代
  • Future Crew传奇之作:Second Reality背后的技术突破与创新
  • 如何用PyCaret与Microsoft Planetary Computer构建环境机器学习解决方案
  • Hoard内存分配器架构解密:如何实现线程安全与高效内存利用的平衡
  • Ursa.Avalonia常见问题解答:从安装到部署的10大痛点解决方案
  • Mocker:革命性Swift网络请求模拟库,让单元测试彻底离线运行
  • Carmine与Redis Cluster集成指南:构建分布式缓存与消息系统
  • 为什么选择Sparky引擎?跨平台游戏开发的5大优势
  • 未来已来:VLC for iOS路线图解读与新功能预测
  • 终极VMware Unlocker完整教程:3步让Windows和Linux轻松运行macOS
  • Citra模拟器终极指南:5个技巧让你的3DS游戏在电脑上飞起来
  • SPIRAN ART SUMMONER参数详解:多画幅生成时分辨率缩放算法对比
  • StructBERT零样本分类-中文-base真实效果:中文外卖评价‘口味/服务/配送/包装’四维度情感识别
  • 春联生成模型-中文-base精彩案例:融合‘一带一路’‘乡村振兴’等时代主题春联
  • 如何轻松避免网络负载过大
  • EVA-01实战案例:设计师用EVA-01解析竞品海报视觉动线与信息层级结构
  • HG-ha/MTools一文详解:AI工具模块底层架构与ONNX Runtime选型逻辑