网站开发需要配置哪些人员,增城新塘网站建设,千图网ppt模板,个人备案网站能做商城吗文章目录 前言一、锁的基本概念1.1 什么是锁#xff1f;1.2 为什么需要锁#xff1f;1.3 锁的作用原理 二、线程锁的类型2.1 自旋锁#xff08;Spin Lock#xff09;2.2 互斥锁#xff08;Mutex#xff09;2.3 混合锁#xff08;Hybrid Lock#xff09;2.4 读写锁… 文章目录 前言一、锁的基本概念1.1 什么是锁1.2 为什么需要锁1.3 锁的作用原理 二、线程锁的类型2.1 自旋锁Spin Lock2.2 互斥锁Mutex2.3 混合锁Hybrid Lock2.4 读写锁Read-Write Lock 三、锁的实现方式3.1 Monitor互斥体3.2 Mutex互斥体3.3 Semaphore信号量3.4 ReaderWriterLock读写锁 四、无锁并发编程4.1 无锁并发编程的概念4.2 无锁算法4.2.1 CASCompare And Swap4.2.2 Volatile 关键字 4.3 无锁并发编程的优势4.4 无锁并发编程的局限性 五、并发集合类5.1 ConcurrentBag5.2 ConcurrentDictionary5.3 ConcurrentQueue5.4 ConcurrentStack 六、经典并发同步问题6.1 生产者-消费者问题Producer-Consumer Problem6.1.1 使用 Monitor 类实现生产者-消费者问题6.1.2 使用 Semaphore 类实现生产者-消费者问题6.1.3 使用 BlockingCollection 类实现生产者-消费者问题 6.2 读者-写者问题Reader-Writer Problem6.2.1 使用 ReaderWriterLockSlim 类实现读者-写者问题6.2.2 使用 SemaphoreSlim 类实现读者-写者问题6.2.3 使用 Monitor 类实现读者-写者问题 6.3 哲学家就餐问题Dining Philosophers Problem6.3.1 使用Semaphore实现哲学家就餐问题6.3.2 使用Mutex实现哲学家就餐问题6.3.3 使用Monitor实现哲学家就餐问题 总结 前言
多线程编程在现代软件开发中至关重要。本文将讨论 C# 中的多线程技术重点介绍锁的概念线程锁与无锁并发。通过学习本篇博文我们将学会如何正确处理并发问题提高程序的性能和稳定性。 一、锁的基本概念
在多线程编程中掌握锁的概念至关重要。本节将介绍什么是锁为什么我们需要锁以及锁的作用原理。
1.1 什么是锁
锁是一种同步机制用于控制多个线程对共享资源的访问。当一个线程获得了锁时其他线程将被阻塞直到该线程释放了锁。
1.2 为什么需要锁
在并发编程中多个线程同时访问共享资源可能导致数据竞争和不确定的行为。锁可以确保在任意时刻只有一个线程可以访问共享资源从而避免竞态条件和数据不一致性问题。
1.3 锁的作用原理
锁的作用原理通常涉及到内部的互斥机制。当一个线程获得锁时它会将锁标记为已被占用其他线程尝试获取该锁时会被阻塞直到持有锁的线程释放锁。这种互斥机制可以通过不同的算法和数据结构来实现如互斥量、自旋锁等。
理解锁的概念是进行多线程编程的基础它为我们提供了一种可靠的方式来保护共享资源确保线程安全和程序的正确性。在接下来的章节中我们将深入探讨不同类型的锁以及它们在 C# 多线程编程中的应用。
二、线程锁的类型
在多线程编程中锁的实现通常基于互斥机制确保在任意时刻只有一个线程可以访问共享资源。本节将介绍几种常见的锁类型包括自旋锁、互斥锁、混合锁和读写锁。
2.1 自旋锁Spin Lock 自旋锁是一种基于忙等待的锁当线程尝试获取锁时如果发现锁已被其他线程占用它会循环自旋等待不断地检查锁是否被释放。自旋锁适用于锁的占用时间短、线程并发度高的情况因为它避免了线程在等待锁时进入内核态造成的性能损失。但自旋锁可能会导致线程空转消耗 CPU 资源因此不适合在锁被占用时间较长或竞争激烈的情况下使用。 2.2 互斥锁Mutex 互斥锁是一种阻塞式锁它通过操作系统提供的原语实现当线程尝试获取锁时如果发现锁已被其他线程占用它会被阻塞直到锁被释放。互斥锁适用于锁的占用时间长、线程竞争激烈的情况因为它可以将等待锁的线程置于休眠状态避免空转浪费 CPU 资源。但互斥锁由于涉及系统调用因此会产生较大的开销尤其在高并发情况下可能成为性能瓶颈。 2.3 混合锁Hybrid Lock 混合锁是结合了自旋锁和互斥锁的优点根据锁的占用情况动态选择使用自旋等待还是阻塞等待。在锁的竞争不激烈时混合锁会采用自旋等待的方式避免线程进入内核态而在锁的竞争激烈时会转为阻塞等待以减少空转和CPU资源的浪费。混合锁的实现较为复杂需要根据具体的场景进行调优以达到最佳的性能和资源利用率。 2.4 读写锁Read-Write Lock 读写锁允许多个线程同时对共享资源进行读取操作但在进行写入操作时需要互斥。读写锁适用于读操作远远多于写操作的场景可以提高程序的并发性能。读写锁通常包含一个写锁和多个读锁当写锁被占用时所有的读锁和写锁都会被阻塞而当读锁被占用时其他的读锁仍然可以被获取但写锁会被阻塞。 三、锁的实现方式
下面是几种常见的锁类型
3.1 Monitor互斥体
Monitor 是 C# 中最基本的锁机制之一它使用 lock 关键字来实现。lock 关键字在进入代码块时获取锁在退出代码块时释放锁。这确保了在同一时刻只有一个线程可以执行 lock 块中的代码。
using System;
using System.Threading;class Program
{private static object _lock new object();static void Main(string[] args){// 启动两个线程访问临界区Thread thread1 new Thread(EnterCriticalSection);Thread thread2 new Thread(EnterCriticalSection);thread1.Start();thread2.Start();}static void EnterCriticalSection(){// 进入临界区Monitor.Enter(_lock);try{// 在临界区内操作共享资源Console.WriteLine($Thread {Thread.CurrentThread.ManagedThreadId} entered critical section.);Thread.Sleep(2000);}finally{// 退出临界区Monitor.Exit(_lock);Console.WriteLine($Thread {Thread.CurrentThread.ManagedThreadId} exited critical section.);}}
}
另一种写法
object lockObj new object();
lock (lockObj)
{// 执行需要同步的代码
}3.2 Mutex互斥体
Mutex 是一种操作系统级别的同步原语与 Monitor 不同Mutex 可以在进程间共享。Mutex 是一个系统对象它可以在全局范围内唯一标识一个锁。使用 Mutex 需要在代码中声明一个 Mutex 对象然后通过 WaitOne 和 ReleaseMutex 方法来获取和释放锁。
using System;
using System.Threading;class Program
{private static Mutex _mutex new Mutex();static void Main(string[] args){// 启动两个线程访问临界区Thread thread1 new Thread(EnterCriticalSection);Thread thread2 new Thread(EnterCriticalSection);thread1.Start();thread2.Start();}static void EnterCriticalSection(){// 等待获取 Mutex_mutex.WaitOne();try{// 在临界区内操作共享资源Console.WriteLine($Thread {Thread.CurrentThread.ManagedThreadId} entered critical section.);Thread.Sleep(2000);}finally{// 释放 Mutex_mutex.ReleaseMutex();Console.WriteLine($Thread {Thread.CurrentThread.ManagedThreadId} exited critical section.);}}
}
3.3 Semaphore信号量
Semaphore 是一种允许多个线程同时访问共享资源的同步原语。它通过一个计数器来控制同时访问资源的线程数量。Semaphore 构造函数需要指定初始的计数器值和最大的计数器值。通过 WaitOne 和 Release 方法来获取和释放信号量。
using System;
using System.Threading;class Program
{private static Semaphore _semaphore new Semaphore(2, 2); // 允许最多两个线程同时访问static void Main(string[] args){// 启动五个线程访问临界区for (int i 0; i 5; i){Thread thread new Thread(EnterCriticalSection);thread.Start(i);}}static void EnterCriticalSection(object threadId){// 等待获取 Semaphore_semaphore.WaitOne();try{// 在临界区内操作共享资源Console.WriteLine($Thread {threadId} entered critical section.);Thread.Sleep(2000);}finally{// 释放 Semaphore_semaphore.Release();Console.WriteLine($Thread {threadId} exited critical section.);}}
}
3.4 ReaderWriterLock读写锁
ReaderWriterLock 是一种特殊的锁机制它允许多个线程同时读取共享资源但在写入资源时需要互斥。这种锁适用于读操作远远多于写操作的场景可以提高性能。
using System;
using System.Threading;class Program
{private static ReaderWriterLockSlim _rwLock new ReaderWriterLockSlim();static void Main(string[] args){// 启动五个读线程和一个写线程访问共享资源for (int i 0; i 5; i){Thread readerThread new Thread(ReadSharedResource);readerThread.Start(i);}Thread writerThread new Thread(WriteSharedResource);writerThread.Start();}static void ReadSharedResource(object threadId){_rwLock.EnterReadLock();try{// 读取共享资源Console.WriteLine($Reader {threadId} read shared resource.);Thread.Sleep(2000);}finally{_rwLock.ExitReadLock();}}static void WriteSharedResource(){_rwLock.EnterWriteLock();try{// 写入共享资源Console.WriteLine(Writer wrote shared resource.);Thread.Sleep(1000);}finally{_rwLock.ExitWriteLock();}}
}
四、无锁并发编程
在多线程编程中除了使用锁机制来保护共享资源外还可以通过无锁并发编程来实现并发控制。本章将介绍无锁并发编程的概念、优势以及常见的无锁算法。
4.1 无锁并发编程的概念
无锁并发编程是一种基于原子操作的并发控制方式它不需要使用传统的锁机制来保护共享资源而是通过原子性操作来确保线程安全。无锁并发编程通常比锁机制具有更低的开销和更高的性能。
4.2 无锁算法
4.2.1 CASCompare And Swap
CAS 是一种原子操作通常由处理器提供支持。它涉及三个操作数内存位置通常是一个地址、旧的预期值和新的值。如果内存位置的值与预期值相等则将新值写入该位置否则操作失败。
using System;
using System.Threading;class Program
{static int sharedValue 0;static void Main(string[] args){// 使用 CAS 算法更新共享变量int expectedValue 0;int newValue 1;if (Interlocked.CompareExchange(ref sharedValue, newValue, expectedValue) expectedValue){Console.WriteLine(Value updated successfully.);}else{Console.WriteLine(Value update failed.);}}
}在代码中Interlocked.CompareExchange 方法用于比较并交换操作它原子性地比较 sharedValue 的值是否等于 expectedValue如果相等则将 newValue 写入 sharedValue并返回原来的值否则不做任何操作。通过这种方式我们可以实现无锁的并发控制避免了锁带来的开销和竞争。 CAS 算法通常用于实现无锁的数据结构例如无锁队列、无锁栈等。虽然 CAS 算法能够提供较好的并发性能但在某些场景下可能会存在ABA问题等限制需要特殊处理。
4.2.2 Volatile 关键字
Volatile 关键字用于声明字段是易变的即可能被多个线程同时访问。它可以确保变量的读取和写入操作都是原子性的并且不会被编译器或者 CPU 优化掉从而避免了线程间的数据不一致性问题。
using System;
using System.Threading;class Program
{private static volatile bool _flag false;static void Main(string[] args){// 启动一个线程不断修改 _flag 的值Thread writerThread new Thread(WriteFlag);writerThread.Start();// 主线程读取 _flag 的值while (true){if (_flag){Console.WriteLine(Flag is true.);break;}else{Console.WriteLine(Flag is false.);Thread.Sleep(1000);}}}static void WriteFlag(){// 在另一个线程中修改 _flag 的值Thread.Sleep(2000);_flag true;Console.WriteLine(Flag has been set to true.);}
}在代码中使用了 volatile 关键字来声明 _flag 字段确保了其在多线程环境下的可见性和原子性。主线程不断读取 _flag 的值而另一个线程在一段时间后将其设置为 true。由于使用了 volatile 关键字主线程能够正确地读取到 _flag 字段的最新值从而实现了线程间的正确通信。 4.3 无锁并发编程的优势
减少线程切换开销无锁并发编程不涉及线程的阻塞和唤醒可以减少线程切换的开销提高程序性能。没有死锁风险由于无锁并发编程不需要使用锁机制因此不存在死锁等与锁相关的问题。
4.4 无锁并发编程的局限性
实现复杂度较高无锁并发编程通常需要仔细设计和实现因此可能比使用锁机制更复杂。适用场景有限无锁并发编程适用于某些特定的场景例如高并发读操作、轻量级状态同步等。
无锁并发编程是一种重要的并发控制方式可以提高程序的性能和可伸缩性。但在实际应用中我们需要根据具体情况选择合适的并发控制方式以确保程序的正确性和性能。
五、并发集合类
在 C# 中.NET Framework 提供了许多线程安全的并发集合类包括 ConcurrentBag、ConcurrentDictionary、ConcurrentQueue 和 ConcurrentStack。本章将介绍这些并发集合类的特点、用途以及示例代码。
5.1 ConcurrentBag
ConcurrentBag 是一个无序的、线程安全的集合类用于存储对象。它允许多个线程同时添加、移除和遍历元素适用于需要高度并发性的场景。
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;class Program
{static void Main(string[] args){ConcurrentBagint bag new ConcurrentBagint();// 使用多个线程添加元素到 ConcurrentBagParallel.For(0, 10, i {bag.Add(i);Console.WriteLine($Added {i} to bag.);});// 遍历 ConcurrentBag 中的元素foreach (var item in bag){Console.WriteLine($Item in bag: {item});}}
}5.2 ConcurrentDictionary
ConcurrentDictionary 是一个线程安全的字典集合类用于存储键值对。它允许多个线程同时对字典进行读取、写入和修改操作提供了高效的并发性能。
using System;
using System.Collections.Concurrent;class Program
{static void Main(string[] args){ConcurrentDictionaryint, string dictionary new ConcurrentDictionaryint, string();// 使用多个线程添加元素到 ConcurrentDictionaryParallel.For(0, 10, i {dictionary.TryAdd(i, i);Console.WriteLine(${i} Added);});// 读取 ConcurrentDictionary 中的键值对foreach (var kvp in dictionary){Console.WriteLine($Key: {kvp.Key}, Value: {kvp.Value});}}
}5.3 ConcurrentQueue
ConcurrentQueue 是一个线程安全的队列集合类用于存储对象。它支持多个线程同时对队列进行入队和出队操作并提供了高效的并发性能。
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;class Program
{static void Main(string[] args){ConcurrentQueueint queue new ConcurrentQueueint();// 使用多个线程入队Parallel.For(0, 10, i {queue.Enqueue(i);Console.WriteLine($Enqueued {i} to queue.);});// 多个线程出队int item;while (queue.TryDequeue(out item)){Console.WriteLine($Dequeued {item} from queue.);}}
}5.4 ConcurrentStack
ConcurrentStack 是一个线程安全的栈集合类用于存储对象。它支持多个线程同时对栈进行入栈和出栈操作并提供了高效的并发性能。
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;class Program
{static void Main(string[] args){ConcurrentStackint stack new ConcurrentStackint();// 使用多个线程入栈Parallel.For(0, 10, i {stack.Push(i);Console.WriteLine($Pushed {i} to stack.);});// 多个线程出栈int item;while (stack.TryPop(out item)){Console.WriteLine($Popped {item} from stack.);}}
}六、经典并发同步问题
以下是几个经典的多线程并发同步问题
6.1 生产者-消费者问题Producer-Consumer Problem
生产者线程生成数据并放入共享缓冲区消费者线程从缓冲区中取出数据进行消费。需要确保在生产者线程生产数据时消费者线程不会访问空缓冲区并且在消费者线程消费数据时生产者线程不会访问满缓冲区。
6.1.1 使用 Monitor 类实现生产者-消费者问题
using System;
using System.Threading;class Program
{static int[] buffer new int[10];static int count 0;static object locker new object();static void Main(string[] args){Thread producerThread new Thread(Producer);Thread consumerThread new Thread(Consumer);producerThread.Start();consumerThread.Start();producerThread.Join();consumerThread.Join();}static void Producer(){for (int i 0; i 20; i){lock (locker){while (count buffer.Length)Monitor.Wait(locker);buffer[count] i;Console.WriteLine(Produced: i);Monitor.PulseAll(locker);}}}static void Consumer(){for (int i 0; i 20; i){lock (locker){while (count 0)Monitor.Wait(locker);int consumed buffer[--count];Console.WriteLine(Consumed: consumed);Monitor.PulseAll(locker);}}}
}6.1.2 使用 Semaphore 类实现生产者-消费者问题
using System;
using System.Threading;class Program
{static int[] buffer new int[10];static SemaphoreSlim empty new SemaphoreSlim(10);static SemaphoreSlim full new SemaphoreSlim(0);static object locker new object();static void Main(string[] args){Thread producerThread new Thread(Producer);Thread consumerThread new Thread(Consumer);producerThread.Start();consumerThread.Start();producerThread.Join();consumerThread.Join();}static void Producer(){for (int i 0; i 20; i){empty.Wait();lock (locker){buffer[i % buffer.Length] i;Console.WriteLine(Produced: i);}full.Release();}}static void Consumer(){for (int i 0; i 20; i){full.Wait();lock (locker){int consumed buffer[i % buffer.Length];Console.WriteLine(Consumed: consumed);}empty.Release();}}
}6.1.3 使用 BlockingCollection 类实现生产者-消费者问题
using System;
using System.Collections.Concurrent;
using System.Threading;class Program
{static BlockingCollectionint buffer new BlockingCollectionint(10);static void Main(string[] args){Thread producerThread new Thread(Producer);Thread consumerThread new Thread(Consumer);producerThread.Start();consumerThread.Start();producerThread.Join();consumerThread.Join();}static void Producer(){for (int i 0; i 20; i){buffer.Add(i);Console.WriteLine(Produced: i);}buffer.CompleteAdding();}static void Consumer(){foreach (var item in buffer.GetConsumingEnumerable()){Console.WriteLine(Consumed: item);}}
}这些示例分别使用了 Monitor、Semaphore 和 BlockingCollection 来解决生产者-消费者问题。每个示例都实现了在生产者线程生成数据时消费者线程不会访问空缓冲区并且在消费者线程消费数据时生产者线程不会访问满缓冲区。
6.2 读者-写者问题Reader-Writer Problem
多个读者线程可以同时读取共享资源但写者线程在写入共享资源时需要独占访问。需要确保在有写者写入时不允许读者读取以保证数据的一致性。
6.2.1 使用 ReaderWriterLockSlim 类实现读者-写者问题
using System;
using System.Threading;class Program
{static ReaderWriterLockSlim rwLock new ReaderWriterLockSlim();static int resource 0;static void Main(string[] args){Thread[] readers new Thread[5];Thread[] writers new Thread[2];for (int i 0; i 5; i){readers[i] new Thread(new ThreadStart(Reader));readers[i].Start();}for (int i 0; i 2; i){writers[i] new Thread(new ThreadStart(Writer));writers[i].Start();}for (int i 0; i 5; i){readers[i].Join();}for (int i 0; i 2; i){writers[i].Join();}}static void Reader(){while (true){rwLock.EnterReadLock();Console.WriteLine(Reader Thread.CurrentThread.ManagedThreadId reads: resource);rwLock.ExitReadLock();Thread.Sleep(1000);}}static void Writer(){while (true){rwLock.EnterWriteLock();resource;Console.WriteLine(Writer Thread.CurrentThread.ManagedThreadId writes: resource);rwLock.ExitWriteLock();Thread.Sleep(2000);}}
}6.2.2 使用 SemaphoreSlim 类实现读者-写者问题
using System;
using System.Threading;class Program
{static SemaphoreSlim readLock new SemaphoreSlim(1);static SemaphoreSlim writeLock new SemaphoreSlim(1);static int readersCount 0;static int resource 0;static void Main(string[] args){Thread[] readers new Thread[5];Thread[] writers new Thread[2];for (int i 0; i 5; i){readers[i] new Thread(new ThreadStart(Reader));readers[i].Start();}for (int i 0; i 2; i){writers[i] new Thread(new ThreadStart(Writer));writers[i].Start();}for (int i 0; i 5; i){readers[i].Join();}for (int i 0; i 2; i){writers[i].Join();}}static void Reader(){while (true){readLock.Wait();readersCount;if (readersCount 1)writeLock.Wait();readLock.Release();Console.WriteLine(Reader Thread.CurrentThread.ManagedThreadId reads: resource);readLock.Wait();readersCount--;if (readersCount 0)writeLock.Release();readLock.Release();Thread.Sleep(1000);}}static void Writer(){while (true){writeLock.Wait();resource;Console.WriteLine(Writer Thread.CurrentThread.ManagedThreadId writes: resource);writeLock.Release();Thread.Sleep(2000);}}
}6.2.3 使用 Monitor 类实现读者-写者问题
using System;
using System.Threading;class Program
{static object lockObj new object();static int readersCount 0;static int resource 0;static void Main(string[] args){Thread[] readers new Thread[5];Thread[] writers new Thread[2];for (int i 0; i 5; i){readers[i] new Thread(new ThreadStart(Reader));readers[i].Start();}for (int i 0; i 2; i){writers[i] new Thread(new ThreadStart(Writer));writers[i].Start();}for (int i 0; i 5; i){readers[i].Join();}for (int i 0; i 2; i){writers[i].Join();}}static void Reader(){while (true){lock (lockObj){readersCount;if (readersCount 1)Monitor.Enter(lockObj);}Console.WriteLine(Reader Thread.CurrentThread.ManagedThreadId reads: resource);lock (lockObj){readersCount--;if (readersCount 0)Monitor.Exit(lockObj);}Thread.Sleep(1000);}}static void Writer(){while (true){Monitor.Enter(lockObj);resource;Console.WriteLine(Writer Thread.CurrentThread.ManagedThreadId writes: resource);Monitor.Exit(lockObj);Thread.Sleep(2000);}}
}6.3 哲学家就餐问题Dining Philosophers Problem
五位哲学家围坐在一张圆桌旁每位哲学家前面有一只筷子。哲学家思考和进餐但只有同时拿到两只筷子时才能进餐而筷子必须是干净的。需要解决资源竞争和死锁的问题。
6.3.1 使用Semaphore实现哲学家就餐问题
using System;
using System.Threading;class Program
{static Semaphore[] sticks new Semaphore[5];static Semaphore table new Semaphore(4, 4);static void Main(string[] args){for (int i 0; i 5; i){sticks[i] new Semaphore(1, 1);}Thread[] philosophers new Thread[5];for (int i 0; i 5; i){philosophers[i] new Thread(Philosopher);philosophers[i].Start(i);}for (int i 0; i 5; i){philosophers[i].Join();}}static void Philosopher(object id){int philosopherId (int)id;while (true){// 思考Console.WriteLine($Philosopher {philosopherId} is thinking.);// 拿筷子table.WaitOne();sticks[philosopherId].WaitOne();sticks[(philosopherId 1) % 5].WaitOne();// 吃饭Console.WriteLine($Philosopher {philosopherId} is eating.);// 放筷子sticks[philosopherId].Release();sticks[(philosopherId 1) % 5].Release();table.Release();Thread.Sleep(2000);}}
}6.3.2 使用Mutex实现哲学家就餐问题
using System;
using System.Threading;class Program
{static Mutex[] sticks new Mutex[5];static void Main(string[] args){for (int i 0; i 5; i){sticks[i] new Mutex();}Thread[] philosophers new Thread[5];for (int i 0; i 5; i){philosophers[i] new Thread(Philosopher);philosophers[i].Start(i);}for (int i 0; i 5; i){philosophers[i].Join();}}static void Philosopher(object id){int philosopherId (int)id;while (true){// 思考Console.WriteLine($Philosopher {philosopherId} is thinking.);// 拿筷子sticks[philosopherId].WaitOne();sticks[(philosopherId 1) % 5].WaitOne();// 吃饭Console.WriteLine($Philosopher {philosopherId} is eating.);// 放筷子sticks[philosopherId].ReleaseMutex();sticks[(philosopherId 1) % 5].ReleaseMutex();Thread.Sleep(2000);}}
}6.3.3 使用Monitor实现哲学家就餐问题
using System;
using System.Threading;class Program
{static object[] sticks new object[5];static void Main(string[] args){for (int i 0; i 5; i){sticks[i] new object();}Thread[] philosophers new Thread[5];for (int i 0; i 5; i){philosophers[i] new Thread(Philosopher);philosophers[i].Start(i);}for (int i 0; i 5; i){philosophers[i].Join();}}static void Philosopher(object id){int philosopherId (int)id;while (true){// 思考Console.WriteLine($Philosopher {philosopherId} is thinking.);lock (sticks[philosopherId]){// 拿左边筷子Monitor.Enter(sticks[philosopherId]);// 拿右边筷子Monitor.Enter(sticks[(philosopherId 1) % 5]);// 吃饭Console.WriteLine($Philosopher {philosopherId} is eating.);// 放筷子Monitor.Exit(sticks[philosopherId]);Monitor.Exit(sticks[(philosopherId 1) % 5]);}Thread.Sleep(2000);}}
}总结
本文简要探讨了 C# 中的多线程编程技术重点介绍了锁的基本概念、线程锁的类型、锁的实现方式、无锁并发编程以及 C# 中的并发集合类和经典并发同步问题。通过学习本文我们可以获得以下几个方面的收获 理解多线程编程的基本概念通过介绍锁的基本概念和原理可以了解为什么在多线程编程中需要使用锁以及锁是如何工作的。 掌握不同类型的线程锁通过对自旋锁、互斥锁、混合锁和读写锁的介绍可以了解各种锁的特点、适用场景和实现方式以便在实际应用中选择合适的锁机制。 熟悉锁的实现方式通过对 Monitor、Mutex、Semaphore 和 ReaderWriterLock 的介绍可以了解不同锁的底层实现原理和使用方法从而更好地应用于实际开发中。 了解无锁并发编程通过介绍无锁算法和无锁并发编程的优势和局限性可以了解在某些场景下无锁编程可以提供更好的性能和并发能力。 熟悉 C# 中的并发集合类通过介绍 ConcurrentBag、ConcurrentDictionary、ConcurrentQueue 和 ConcurrentStack 等并发集合类可以了解如何安全地在多线程环境中使用集合类。 解决经典并发同步问题通过介绍生产者-消费者问题、读者-写者问题和哲学家就餐问题的解决方案可以了解如何使用线程锁来解决实际的并发同步问题。 通过本文的学习可以更加深入地理解并发编程的相关知识掌握多线程编程的技巧提高程序的性能和稳定性。