






















在实际开发中,线程就像一群工人,大家都要用工具、机器、库房。如果没有秩序——工人们可能会:
同步构造就是工地的规章制度+工具,让工人们有序工作。
volatile —— 全局开关📌 真实例子:一个服务里的后台线程循环检查“是否关闭系统”。
private volatile bool _shutdownRequested;
[Button("StartWorker")]
public void StartWorker()
{
new Thread(() =>
{
while (!_shutdownRequested)
{
// 检查队列、做点小任务
Thread.Sleep(1000);
Debug.Log("后台线程正在运行...");
}
Debug.Log("后台线程安全退出");
}).Start();
}
[Button("StopWorker")]
public void Stop() => _shutdownRequested = true;
[Button("StartRequest")]
public void StartRequest()=>_shutdownRequested = false;
👉 实战场景:游戏服务器的 后台守护线程,用它来安全退出。
Interlocked —— 安全计数器📌 真实例子:统计 Web API 调用次数(并发请求)。
private static int _requestCount = 0;
public void HandleRequest()
{
Interlocked.Increment(ref _requestCount);
Console.WriteLine($"总请求数: {_requestCount}");
}
private volatile bool _shutdownRequested;
private static int _requestCount=0;
public void HandleRequest()=> Interlocked.Increment(ref _requestCount);
[Button("StartWorker")]
public void StartWorker()
{
Random random = new Random();
new Thread(() =>
{
while (!_shutdownRequested)
{
//模拟并发计数
int requestCount = random.Next(1,5);
Parallel.For(0, requestCount, i =>
{
HandleRequest();
});
// 检查队列、做点小任务
Thread.Sleep(1000);
Debug.Log($"总请求数: {_requestCount}后台线程正在运行...");
}
Debug.Log("后台线程安全退出");
}).Start();
}
👉 实战场景:Web/游戏服里统计访问量、在线人数、任务完成数。
SpinLock —— 短时间的临界资源📌 真实例子:游戏引擎里一个对象池,线程从池子里拿/还对象,操作极快。
private static SpinLock _spinLock = new SpinLock();
private static Queue<int> _pool = new Queue<int>(Enumerable.Range(1, 10));
public static int Rent()
{
bool taken = false;
try
{
_spinLock.Enter(ref taken);
return _pool.Dequeue();
}
finally
{
if (taken) _spinLock.Exit();
}
}
👉 场景:高频极短操作(比如 Unity 里粒子、子弹对象池)。
lock —— 共享资源保护📌 真实例子:日志系统,多个线程写同一个文件。
private static readonly object _logLock = new object();
public static void WriteLog(string msg)
{
lock (_logLock)
{
File.AppendAllText("log.txt", $"{DateTime.Now}: {msg}\n");
}
}
👉 场景:写文件、更新全局配置、写数据库事务。
ManualResetEvent(手工的,手动的) —— 发信号,多个线程同时出发📌 真实例子:赛车游戏起跑线,所有选手等裁判开枪。
ManualResetEvent startSignal = new ManualResetEvent(false);
void Runner(string name)
{
Console.WriteLine($"{name} 准备就绪...");
startSignal.WaitOne();
Console.WriteLine($"{name} 出发!");
}
// 创建选手线程
new Thread(() => Runner("A")).Start();
new Thread(() => Runner("B")).Start();
// 裁判发令
Thread.Sleep(1000);
Console.WriteLine("裁判开枪!");
startSignal.Set();
👉 场景:并发任务同时开始,如压测工具模拟 1000 个玩家同时登录。
AutoResetEvent —— 一个接一个📌 真实例子:打印机一次只能打印一个文件,但多个线程要打印。
AutoResetEvent printer = new AutoResetEvent(true);
void Print(string doc)
{
printer.WaitOne();
Console.WriteLine($"正在打印: {doc}");
Thread.Sleep(1000);
Console.WriteLine($"{doc} 完成");
printer.Set();
}
new Thread(() => Print("文件1")).Start();
new Thread(() => Print("文件2")).Start();
new Thread(() => Print("文件3")).Start();
👉 场景:串行访问硬件/IO。
Semaphore(打信号,打旗语) —— 有限资源池📌 真实例子:数据库连接池(最多 3 个连接)。
Semaphore dbConnections = new Semaphore(3, 3);
void QueryDB(int id)
{
dbConnections.WaitOne();
Console.WriteLine($"线程{id} 使用数据库连接...");
Thread.Sleep(2000);
Console.WriteLine($"线程{id} 释放连接");
dbConnections.Release();
}
for (int i = 0; i < 6; i++)
{
int id = i;
new Thread(() => QueryDB(id)).Start();
}
👉 场景:并发下载、数据库连接池、线程池限流。
Mutex (互斥)—— 跨进程大锁📌 真实例子:防止同一程序被重复启动。
static void Main()
{
using var mutex = new Mutex(false, "Global\\MyUniqueApp");
if (!mutex.WaitOne(0, false))
{
Console.WriteLine("程序已在运行!");
return;
}
Console.WriteLine("程序运行中...");
Console.ReadLine();
}
👉 场景:跨进程互斥,如防止多开。
| ⚔️ 武器 | 比喻 | 实战例子 |
|---|---|---|
volatile、InterlockedlockSemaphoreMutex(慎用)Interlocked 统计已完成任务数。Semaphore 控制最多 N 个并发请求。lock 确保文件写安全。Task.Wait() 或 Result?答案:
因为它们会阻塞当前线程,而 UI/游戏主线程负责渲染和输入循环,一旦阻塞,就会造成界面卡死(Unity 会掉帧)。
解析:
Task.Wait()/Result = 阻塞等待,常见于控制台/后台任务。
在 UI/游戏主线程应使用 await(异步等待),不会阻塞消息泵或渲染循环。
推荐写法:
var data = await File.ReadAllTextAsync("config.json");
volatile 和 Interlocked 的区别是什么?分别适用什么场景?答案:
volatile:保证读写总是直接访问内存,避免缓存不一致;不保证原子性。Interlocked:保证复合操作(加减、交换、CAS)原子性。解析:
volatile 即可。Interlocked,否则 ++ 操作会发生竞争。示例:
private volatile bool _stopping; // 状态标志
private int _counter = 0; // 计数器
// 停止标志
if (_stopping) return;
// 线程安全计数
Interlocked.Increment(ref _counter);
SpinLock 为什么可能比 lock 更快?又有什么缺点?答案:
lock 更快。解析:
SpinLock 适合 对象池取还、短暂标志交换等极快操作。lock(Monitor)适合常规业务逻辑,因为它会把线程挂起,等待时不占 CPU。ManualResetEvent 和 AutoResetEvent 的区别,并给出一个应用场景。答案:
ManualResetEvent:信号保持开启,唤醒所有等待线程。AutoResetEvent:信号一次性,只唤醒一个线程,之后自动重置。解析:
ManualResetEvent 场景:模拟比赛起跑信号,所有线程同时开始。AutoResetEvent 场景:打印机任务队列,一次只允许一个线程打印。示例:
AutoResetEvent printer = new AutoResetEvent(true);
void Print(string doc)
{
printer.WaitOne();
Console.WriteLine($"打印 {doc}");
Thread.Sleep(1000);
printer.Set();
}
答案:
使用 Semaphore 来控制可同时进入的线程数。
解析:
Semaphore 可以定义最大并发数,例如 10。示例:
Semaphore dbPool = new Semaphore(10, 10);
void HandleQuery()
{
dbPool.WaitOne(); // 占用一个名额
try
{
Console.WriteLine("查询数据库...");
Thread.Sleep(500); // 模拟查询
}
finally
{
dbPool.Release(); // 释放连接
}
}
await 才是正解。volatile 用于标志位,Interlocked 用于计数器。SpinLock = 临界区极短时快,否则耗 CPU。ManualResetEvent = 同时唤醒,AutoResetEvent = 一个一个。Semaphore 控制有限资源池(连接池/下载池)。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。