02、线程同步概述

线程同步概述

知识点

什么是线程同步

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

为什么需要线程同步

  1. 竞态条件(Race Condition):多个线程同时访问共享资源时可能产生不确定的结果
  2. 数据不一致:没有同步机制时,多线程可能导致数据状态不一致
  3. 内存可见性:一个线程对共享变量的修改对其他线程不一定立即可见

常见的同步问题

  • 死锁(Deadlock):两个或多个线程相互等待对方释放资源
  • 活锁(Livelock):线程不断重试但无法取得进展
  • 饥饿(Starvation):某些线程长时间无法获得所需资源

.NET中的同步机制分类

  1. 基础同步原语:lock、Monitor、Mutex
  2. 信号通知:AutoResetEvent、ManualResetEvent、Semaphore
  3. 读写同步:ReaderWriterLock、ReaderWriterLockSlim
  4. 原子操作:Interlocked、volatile
  5. 高级同步: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("任务成功完成,没有发生死锁。");
        }
    }
}

知识点总结

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

  2. 常见同步问题:

    • 竞态条件:多线程并发访问共享资源导致的不确定结果
    • 死锁:线程间相互等待造成的僵局
    • 活锁和饥饿:资源分配不当导致的性能问题
  3. 同步机制选择:

    • 简单互斥:使用lock或Monitor
    • 原子操作:使用Interlocked类
    • 复杂同步:使用专门的同步原语
  4. 性能考虑:

    • 同步会带来性能开销
    • 选择合适的同步机制很重要
    • 避免过度同步
  5. 最佳实践:

    • 尽量减少共享状态
    • 保持锁的粒度适当
    • 避免嵌套锁以防止死锁
    • 使用超时机制防止无限等待
posted @ 2025-08-27 01:24  jessqiu  阅读(52)  评论(0)    收藏  举报