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

区分同步(Synchronous)和异步(Asynchronous)

目录

区分同步(Synchronous)和异步(Asynchronous)

1.同步(Synchronous)

1.1.概念

1.2.同步在普通代码中的表现

1.3.同步在线程中的限制

2.异步(Asynchronous)

2.1.概念

2.2.实现异步的两种方式

2.2.1.协程(Coroutine):单线程内的异步模拟

1)协程的异步原理

2)协程异步的使用场景

3)示例:协程异步处理分帧任务

2.2.2.线程(Thread):多线程并行的异步

1)线程的异步原理

2)线程异步的使用场景

3)示例:线程异步读取文件

3.同步 VS 异步

4.协程异步 VS 线程异步(在Unity中的关键区别)

5.如何选择同步、协程异步、线程异步?


      在Unity和C#中,同步(Synchronous)和异步(Asynchronous)是描述代码执行流程的核心概念,直接影响程序的响应性、性能和资源利用率。结合协程(Coroutine)和线程(Thread)的使用场景,可以更清晰地理解它们的区别和适用场景。

Unity和C#中的同步(Synchronous)和异步(Asynchronous)

1.同步(Synchronous)

1.1.概念

(1)概述:同步是“按顺序等待”的执行模式

(2)定义:代码按照编写顺序依次执行,前一个任务未完成时,后续任务必须等待,直到前一个任务执行完毕才能继续。

(3)核心特点:单任务阻塞,执行流程直观但可能导致卡顿。

1.2.同步在普通代码中的表现

       在Unity的Update、Start等方法中,默认都是同步执行

using System.Threading;
using UnityEngine;public class Test1: MonoBehaviour
{ private void Start(){Task1();//执行任务1(耗时2秒)Task2();//必须等待任务1完成后才执行}private void Task1(){Debug.Log("任务1开始");//模拟耗时操作(如大量计算)Thread.Sleep(2000);//阻塞主线程2秒Debug.Log("任务1完成");}private void Task2(){Debug.Log("任务2完成");}
}

       执行结果:Task1执行时会阻塞主线程2秒,期间游戏画面卡顿,Task2必须等Task1完成后才执行。注意:若Task1是耗时操作(如文件读写、复杂计算),会直接导致游戏冻结,严重影响体验。

1.3.同步在线程中的限制

       线程本身可以并行执行,但线程内部的代码默认是同步的

using System.Threading;
using UnityEngine;public class Test2: MonoBehaviour
{private void Start(){//启动子线程执行同步任务new Thread(SyncThreadTask).Start();}private void SyncThreadTask(){Step1();//必须完成后才能执行Step2Step2();}private void Step1() { /* 耗时操作 */ Debug.Log("Step1():执行");}private void Step2() { /* 依赖Step1的结果 */ Debug.Log("Step2():执行");}
}

       子线程内部的Step1和Step2是同步执行的,但子线程的执行不会阻塞主线程(这是线程与同步的区别)。

2.异步(Asynchronous)

2.1.概念

(1)概述:异步是“不等待,先做其他事”的执行模式

(2)定义:代码执行到耗时操作时,不阻塞当前线程,而是让线程继续处理其他任务,待耗时操作完成后再“回调”或“恢复”执行后续逻辑

(3)核心特点:非阻塞,提高线程利用率,避免卡顿。全程线程不“同时做两件事”,只是利用“耗时操作的等待时间”做了其他任务,避免卡顿,提高效率。

用“主线程+协程加载资源”理解

(1)初始执行:主线程执行协程代码,调用“异步加载资源”接口(比如Resources.LoadAsync<Texture2D>("image"))。

(2)委托耗时操作:主线程把“加载资源”这个耗时任务交给Unity的“资源加载管理器”(引擎层面的组件),然后“暂停协程的后续逻辑”。

(3)处理其他任务:主线程转而去执行“其他任务”(比如执行Update、渲染、UI交互),此时资源加载由“资源加载管理器”在后台处理,主线程完全不参与等待。

(4)恢复执行后续逻辑:当“资源加载管理器”完成加载后,会通知Unity的协程调度器;协程调度器在主线程的“下一个合适时机”(比如EndOfFrame或FixedUpdate之后),让主线程“恢复执行协程暂停后的代码”(比如把加载好的纹理赋值给Sprite)。

注意:

       线程同一时间只能做一件事。整个过程中,主线程始终只做一件事:要么处理Update/渲染/UI交互,要么恢复协程后续逻辑,没有“同时加载资源和渲染”的情况。

2.2.实现异步的两种方式

       在Unity和C#中,异步主要通过两种方式实现:协程(Coroutine)和多线程(Thread),但两者的异步机制有本质区别。

2.2.1.协程(Coroutine):单线程内的异步模拟

Unity协程是单线程异步的典型实现,依赖yield return关键字主动“让出执行权”,本质是“协作式异步”

1)协程的异步原理

       协程运行在Unity主线程中,不会创建新线程。通过yield return暂停自身执行,让主线程继续处理其他逻辑(如渲染、输入响应)。满足yield return的条件后(如等待2秒、等待帧结束),Unity会在下一帧从暂停处恢复协程执行。

2)协程异步的使用场景

①分帧加载:如“将10000条数据分10帧加载,避免单帧卡顿”。

②延迟操作:如“3秒后播放技能特效”。

③等待异步事件:如“等待资源加载完成后刷新UI”、“等待网络请求响应”

3)示例:协程异步处理分帧任务
using System.Threading;
using UnityEngine;public class Test3: MonoBehaviour
{private void Start(){List<string> largeDataList = new List<string>();for (int i = 0; i < 10000; i++){largeDataList.Add($"数据项 {i}");}StartCoroutine(ProcessLargeDataAsync(largeDataList));Debug.Log("协程已启动,主线程继续执行其他任务");//立即执行,不等待协程完成}private IEnumerator ProcessLargeDataAsync(List<string> dataList){int total = dataList.Count;int batchSize = 100;//每帧处理100条数据for (int i = 0; i < total; i += batchSize){//处理当前批次数据(同步执行,但只占一帧的部分时间)for (int j = i; j < Mathf.Min(i + batchSize, total); j++){ProcessData(dataList[j]);}//让出执行权,等待下一帧再继续(异步的核心)yield return null;Debug.Log($"已处理 {Mathf.Min(i + batchSize, total)}/{total} 条数据");}}private void ProcessData(string data){}
}

       异步表现:ProcessLargeDataAsync执行时,每处理100条数据就暂停,主线程可以继续渲染画面、响应输入,避免卡顿。

2.2.2.线程(Thread):多线程并行的异步

       线程是操作系统级别的异步,通过创建新线程实现“真正的并行”(多核CPU下),本质是“抢占式异步”

1)线程的异步原理

       线程由操作系统调度,可独立于主线程运行在不同CPU核心上。主线程无需等待子线程完成,两者可以并行执行(真正的异步)。子线程执行耗时操作(如文件读写、网络请求)时,主线程不受影响。

2)线程异步的使用场景

①CPU密集型任务:如大规模数据计算、图像处理(利用多核提升效率)。

②IO密集型任务:如读取大文件、下载网络资源(避免阻塞主线程)。

3)示例:线程异步读取文件
/// <summary>
/// 主线程调度器:用于子线程向主线程投递任务(如更新 UI)
/// </summary>
public class MainThreadDispatcher : MonoBehaviour
{//单例实例private static MainThreadDispatcher instance;//主线程任务队列private readonly Queue<System.Action> mainThreadTasks = new Queue<System.Action>();//确保场景中只有一个调度器private void Awake(){if (instance == null){instance = this;}else{Destroy(gameObject);}}//主线程每帧执行任务队列private void Update(){lock (mainThreadTasks)//线程安全地访问任务队列{while (mainThreadTasks.Count > 0){//执行主线程任务(如更新 UI)mainThreadTasks.Dequeue()?.Invoke();}}}/// <summary>/// 向主线程投递任务/// </summary>/// <param name="task">要在主线程执行的任务(如 UI 操作)</param>public static void EnqueueTask(System.Action task){if (instance == null){Debug.LogError("MainThreadDispatcher 未初始化!请在场景中添加该脚本");return;}lock (instance.mainThreadTasks){instance.mainThreadTasks.Enqueue(task);}}
}
public class FileReaderThread : MonoBehaviour
{public TextMeshProUGUI resultText;//结果文本,显示文件内容private void Start(){Debug.Log("主线程开始,启动文件读取线程");//启动子线程执行异步文件读取new Thread(()=>ReadFileAsync(Path.Combine(Application.streamingAssetsPath, "file.txt"))).Start();Debug.Log("主线程继续处理其他任务(如UI更新)");//立即执行,不等待子线程}//子线程:异步读取大文件(不阻塞主线程)private void ReadFileAsync(string filePath){byte[] data = File.ReadAllBytes(filePath);//耗时IO操作Debug.Log($"子线程读取完成,文件大小:{data.Length}字节");//注意:子线程不能直接操作Unity API(如更新UI),需通过主线程调度器MainThreadDispatcher.EnqueueTask(() =>{resultText.text += $"文件:{Path.GetFileName(filePath)}\n内容:{File.ReadAllText(filePath)}\n\n"; ;//在主线程更新UI});}
}

       异步表现:ReadFileAsync在子线程中执行耗时的文件读取,主线程可以继续处理UI渲染、用户输入,游戏画面保持流畅。

3.同步 VS 异步

对比维度

同步(Synchronous)

异步(Asynchronous)

执行方式

按顺序执行,前一个任务阻塞后续任务

不阻塞,任务执行时允许其他操作并行/并发执行

线程关系

单线程内顺序执行,无线程切换

协程:单线程内通过yield切换任务

线程:多线程并行

阻塞风险

耗时任务会阻塞当前线程(如主线程卡顿)

耗时任务不阻塞当前线程(协程让出执行权,线程独立运行)

适用场景

简单逻辑、无耗时操作的任务(如变量赋值、简单计算)

耗时操作(延迟、IO、计算)、需保持响应性的场景

Unity 限制

无特殊限制,但耗时操作会导致卡顿

协程:必须在主线程,可调用Unity API

线程:不可直接调用Unity API

4.协程异步 VS 线程异步(在Unity中的关键区别)

特性

协程(Coroutine)

线程(Thread)

线程数量

运行在Unity主线程,不创建新线程

创建新线程,独立于主线程

Unity API 调用

可以直接调用(如Instantiate、transform.position)

禁止直接调用(会导致崩溃或异常),需通过主线程调度

数据安全

单线程内执行,无数据竞争问题

多线程共享数据时需加锁(lock),否则可能数据错乱

性能开销

极低(仅维护执行上下文)

较高(线程创建、切换、内存占用)

适用任务

短耗时、需与 Unity 生命周期交互的任务(如分帧加载、延迟操作、等待异步事件)

长耗时、独立于Unity生命周期的任务(如CPU密集型任务、IO密集型任务)

5.如何选择同步、协程异步、线程异步?

(1)同步:适用于无耗时操作的简单逻辑(如变量赋值、条件判断),直接顺序执行即可。

(2)协程异步:适用于需要与Unity交互且耗时较短的异步任务(如分帧加载、延迟操作、等待异步事件),优点是简单、安全,无需处理线程同步。

(3)线程异步:适用于完全独立于Unity生命周期且耗时长的任务,CPU密集型任务、IO密集型任务,优点是不阻塞主线程,但需注意线程安全和Unity API限制。

       在实际开发中,三者往往结合使用,如用线程异步下载资源,下载完成后通过协程在主线程分帧加载资源并更新UI,既保证效率又避免卡顿。想要进一步了解进程、线程和协程可以查看【一文了解】Unity的协程(Coroutine)与线程(Thread)和区分进程(Process)、线程(Thread)和协程(Coroutine)

       好了,本次的分享到这里就结束啦,希望对你有所帮助~

http://www.dtcms.com/a/390191.html

相关文章:

  • 隐语开源隐私计算SecretFlow,实测性能提升10倍,纵向联邦SecureBoost算法(已开源)
  • 云南食品安全管理员考试都考哪些知识点
  • AAAI2025 | 视觉语言模型 | 西电等提出少样本语言驱动多模态分类模型DiffCLIP
  • Coze(扣子)零基础开发02-建一个简单机器人
  • 混合架构(SpringCloud+Dubbo)的整合方案与适用场景(三)
  • SPI 通信协议
  • vue3学习日记(十六):路由配置详解
  • 河南省 ERA5 日值气象数据处理教程(2020–2025)
  • 继承和多态常见面试问题解析
  • 博士生如何进行文献阅读和文献整理?
  • 矩阵分析线性表示例题
  • OpenEuler---jumpserver堡垒机部署
  • STM32 驱动 MAX31865 读取 PT100 温度方案
  • 第四次编程记录
  • 2024年7月 自旋散射效应
  • 理解神经网络中的批量数据处理:维度、矩阵乘法与广播机制
  • UDP传输大数据?真的能兼顾速度和可靠性吗?
  • 某税网登录逆向-sm2-HMacSHA256-sm4-滑块
  • HashMap 添加元素put()的源码和扩容方法resize()的源码解析
  • Windows系统如何查看SSH公钥?
  • 苹果软件代码混淆与多框架应用加固 iOS混淆、ipa文件安全、跨端应用安全防护全流程指南
  • 第一章 神经网络的复习:神经网络的推理
  • MinIO 4 节点集群部署实战:RPM 安装 + mc 工具攻略(网站托管、自动备份)
  • 支持向量机 SVM 预测人脸数据集时数据是否标准化的对比差异
  • 学习笔记:Vue 透传
  • 【记录59】携带token加载图片、图片过大自行压缩、转base64、
  • CentOS 7下FTP配置全攻略
  • 利用Debezium和PostgreSQL逻辑复制实现实时数据同步架构设计与优化实践
  • Part05 数学与其他
  • 链接脚本总结