02、线程同步概述

线程同步概述

知识点

什么是线程同步

线程同步是多线程编程中的核心概念,指的是协调多个线程对共享资源的访问,确保数据的一致性和程序的正确性。

为什么需要线程同步

竞态条件(Race Condition):多个线程同时访问共享资源时可能产生不确定的结果

数据不一致:没有同步机制时,多线程可能导致数据状态不一致

内存可见性:一个线程对共享变量的修改对其他线程不一定立即可见

常见的同步问题

死锁(Deadlock):两个或多个线程相互等待对方释放资源

活锁(Livelock):线程不断重试但无法取得进展

饥饿(Starvation):某些线程长时间无法获得所需资源

.NET中的同步机制分类

基础同步原语:lock、Monitor、Mutex

信号通知:AutoResetEvent、ManualResetEvent、Semaphore

读写同步:ReaderWriterLock、ReaderWriterLockSlim

原子操作:Interlocked、volatile

高级同步:Barrier、CountdownEvent、SemaphoreSlim

代码案例

案例1:没有同步的竞态条件

using System;

using System.Threading;

using System.Threading.Tasks;

class Program

{

private static int counter = 0;

static void Main(string[] args)

{

// 启动多个任务同时修改共享变量

Task[] tasks = new Task[10];

for (int i = 0; i < 10; i++)

{

tasks[i] = Task.Run(() =>

{

for (int j = 0; j < 1000; j++)

{

counter++; // 非原子操作,可能产生竞态条件

}

});

}

Task.WaitAll(tasks);

Console.WriteLine($"期望结果: 10000");

Console.WriteLine($"实际结果: {counter}"); // 通常小于10000

Console.WriteLine("由于竞态条件,实际结果可能小于期望结果");

}

}

案例2:使用lock解决同步问题

using System;

using System.Threading;

using System.Threading.Tasks;

class Program

{

private static int counter = 0;

private static readonly object lockObject = new object();

static void Main(string[] args)

{

Task[] tasks = new Task[10];

for (int i = 0; i < 10; i++)

{

tasks[i] = Task.Run(() =>

{

for (int j = 0; j < 1000; j++)

{

lock (lockObject) // 使用lock确保线程安全

{

counter++;

}

}

});

}

Task.WaitAll(tasks);

Console.WriteLine($"期望结果: 10000");

Console.WriteLine($"实际结果: {counter}"); // 总是等于10000

Console.WriteLine("使用lock后,结果正确");

}

}

案例3:使用Interlocked进行原子操作

using System;

using System.Threading;

using System.Threading.Tasks;

class Program

{

private static int counter = 0;

static void Main(string[] args)

{

Task[] tasks = new Task[10];

for (int i = 0; i < 10; i++)

{

tasks[i] = Task.Run(() =>

{

for (int j = 0; j < 1000; j++)

{

Interlocked.Increment(ref counter); // 原子递增操作

}

});

}

Task.WaitAll(tasks);

Console.WriteLine($"期望结果: 10000");

Console.WriteLine($"实际结果: {counter}"); // 总是等于10000

Console.WriteLine("使用Interlocked.Increment确保了原子性");

}

}

案例4:演示死锁问题

using System;

using System.Threading;

using System.Threading.Tasks;

class Program

{

private static readonly object lock1 = new object();

private static readonly object lock2 = new object();

static void Main(string[] args)

{

Console.WriteLine("演示死锁问题...");

Task task1 = Task.Run(() =>

{

lock (lock1)

{

Console.WriteLine("Task1: 获得了lock1");

Thread.Sleep(1000); // 模拟一些工作

lock (lock2)

{

Console.WriteLine("Task1: 获得了lock2");

}

}

});

Task task2 = Task.Run(() =>

{

lock (lock2)

{

Console.WriteLine("Task2: 获得了lock2");

Thread.Sleep(1000); // 模拟一些工作

lock (lock1)

{

Console.WriteLine("Task2: 获得了lock1");

}

}

});

// 等待5秒,如果任务未完成则可能发生了死锁

if (!Task.WaitAll(new[] { task1, task2 }, TimeSpan.FromSeconds(5)))

{

Console.WriteLine("检测到死锁!任务未能在5秒内完成。");

}

else

{

Console.WriteLine("任务成功完成,没有发生死锁。");

}

}

}

知识点总结

线程同步的重要性:在多线程环境中,线程同步是确保程序正确性的关键机制

常见同步问题:

竞态条件:多线程并发访问共享资源导致的不确定结果

死锁:线程间相互等待造成的僵局

活锁和饥饿:资源分配不当导致的性能问题

同步机制选择:

简单互斥:使用lock或Monitor

原子操作:使用Interlocked类

复杂同步:使用专门的同步原语

性能考虑:

同步会带来性能开销

选择合适的同步机制很重要

避免过度同步

最佳实践:

尽量减少共享状态

保持锁的粒度适当

避免嵌套锁以防止死锁

使用超时机制防止无限等待


戰車世界
双端跨服大乱斗!《天下》手游第22届季后赛赛况正如火如茶!