【一文了解】线程的使用
目录
线程的使用
1.基础语法
1.1.引用命名空间
1.2.创建线程的3种方式
(1)无参数方法
(2)带参数方法(参数必须是object类型)
(3)使用匿名函数(Lambda表达式)
2.线程的核心操作
2.1.线程等待(主线程等待子线程完成)
2.2.线程休眠(暂停执行一段时间)
2.3.强制终止线程(不推荐)
2.4.安全终止线程(推荐)
3.Unity中使用线程的特殊注意事项
3.1.禁止子线程调用UnityAPI
3.2.子线程与主线程通信(安全更新UI)
4.总结
本篇文章分享一下线程该怎么使用。
线程的使用
线程是进程内的最小执行单元,一个进程可以包含多个线程,所有线程共享该进程的内存空间和资源(如变量、文件句柄)。线程是操作系统调度的基本单位(CPU直接执行线程)。
1.基础语法
1.1.引用命名空间
在C#中,线程(Thread)的使用主要依赖System.Threading命名空间下的Thread类。
using System.Threading;
1.2.创建线程的3种方式
线程的核心是“要执行的方法”,可以通过以下方式创建并绑定方法:
(1)无参数方法
//定义线程要执行的方法(无参数)
private void ThreadTask()
{Debug.Log($"子线程执行,线程ID:{Thread.CurrentThread.ManagedThreadId}");
}//创建并启动线程
Thread thread = new Thread(ThreadTask);//创建线程并绑定方法
thread.Start();//启动线程(线程进入就绪状态,等待CPU调度)
(2)带参数方法(参数必须是object类型)
//定义带参数的方法(参数类型必须为 object)
private void ThreadTaskWithParam(object message)
{string msg = (string)message; // 强制类型转换Debug.Log($"子线程收到消息:{msg},线程ID:{Thread.CurrentThread.ManagedThreadId}");
}//创建并启动线程(传递参数)
Thread thread = new Thread(ThreadTaskWithParam);//创建线程并绑定方法
thread.Start("Hello from main thread");//启动时传入参数
(3)使用匿名函数(Lambda表达式)
//直接通过 Lambda 定义线程任务(适合简单逻辑)
Thread thread = new Thread(() =>
{Debug.Log($"匿名线程执行,线程ID:{Thread.CurrentThread.ManagedThreadId}");
});
thread.Start();
2.线程的核心操作
2.1.线程等待(主线程等待子线程完成)
使用Join()方法让主线程阻塞,直到子线程执行完毕
Thread thread = new Thread(() =>
{Thread.Sleep(2000);//子线程休眠2秒(模拟耗时操作)Debug.Log("子线程完成");
});thread.Start();
Debug.Log("主线程等待子线程完成...");
thread.Join();//主线程阻塞,直到子线程执行完毕
Debug.Log("主线程继续执行");
2.2.线程休眠(暂停执行一段时间)
使用Thread.Sleep(int milliseconds)让当前线程暂停指定毫秒数
Thread thread = new Thread(() =>
{Debug.Log("子线程开始");Thread.Sleep(1000);//暂停1秒Debug.Log("子线程1秒后继续");
});
thread.Start();
2.3.强制终止线程(不推荐)
使用Abort()强制终止线程,可能导致资源未释放,建议通过标志位安全终止
Thread thread = new Thread(() =>
{try{while (true){Debug.Log("子线程运行中...");Thread.Sleep(500);}}catch (ThreadAbortException){Debug.Log("子线程被强制终止");}
});thread.Start();
Thread.Sleep(2000);//主线程等待2秒
thread.Abort();//强制终止子线程
2.4.安全终止线程(推荐)
通过bool标志位控制线程退出,避免资源泄漏
private bool isThreadRunning = true;//线程运行标志位private void StartThread()
{Thread thread = new Thread(LoopTask);thread.Start();
}private void LoopTask()
{while (isThreadRunning)//通过标志位控制循环{Debug.Log("子线程运行中...");Thread.Sleep(500);}Debug.Log("子线程安全退出");
}//外部调用此方法终止线程
private void StopThread()
{isThreadRunning = false;
}
3.Unity中使用线程的特殊注意事项
3.1.禁止子线程调用UnityAPI
Unity绝大多数API(如Transform、GameObject、Debug.Log等)只能在主线程调用,子线程调用会报错
//错误示例:子线程调用 Unity API
Thread thread = new Thread(() =>
{//以下代码会导致崩溃!GameObject obj = new GameObject("Test"); Debug.Log("子线程调用Unity API");
});
thread.Start();
3.2.子线程与主线程通信(安全更新UI)
通过“主线程调度器”将UI操作委托给主线程执行,如下例:文件读取与展示
(1)实现主线程调度器
using UnityEngine;
using System.Collections.Generic;/// <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);}}
}
(2)子线程通过调度器更新UI
using UnityEngine;
using System.IO;
using System.Threading;
using TMPro;/// <summary>
/// 文件读取子线程
/// </summary>
public class FileReaderThread:MonoBehaviour
{public TextMeshProUGUI statusText;public TextMeshProUGUI resultText;private void Start(){resultText.text = "准备读取文件...";Thread thread = new Thread(FileReadThreadTask);statusText.text = "开始读取文件";thread.Start();}private void FileReadThreadTask(){Thread.Sleep(2000);string filePath =Path.Combine(Application.streamingAssetsPath, "file.txt");string content = File.ReadAllText(filePath);MainThreadDispatcher.EnqueueTask(() =>{resultText.text = content;statusText.text = "文件读取完成!";});}
}
4.总结
线程主要用于处理“不涉及UnityAPI的耗时任务”(CPU密集型任务,如数据计算;IO密集型任务,如文件读写),使用时需注意线程安全(共享数据加锁)和Unity API调用限制。
想要进一步了解进程、线程和协程可以查看【一文了解】Unity的协程(Coroutine)与线程(Thread)和区分进程(Process)、线程(Thread)和协程(Coroutine)
好了,本次的分享到这里就结束啦,希望对你有所帮助~