2025-10-11
C#
00

在现代 C# 多线程编程中,Task 类是处理异步操作的重要工具。Task.WaitAny 方法允许你等待多个 Task 之一完成,这在处理并行任务时非常有用。本文将详细介绍 Task.WaitAny 的特点、用法,并提供多个示例以展示其应用。

Task.WaitAny 的特点

  • 等待任意任务完成Task.WaitAny 可以接收一个任务数组,并在任一任务完成时立即返回。这在需要处理并行任务的场合提供灵活性。
  • 非阻塞等待:虽然这个方法等待一个或多个任务完成,但它能以非阻塞的方式执行其他操作,帮助提高应用的响应性。
  • 返回完成任务的索引:当某个任务完成时,Task.WaitAny 返回完成任务在任务数组中的索引。
  • 支持超时Task.WaitAny 可以设置超时参数,指定等待时间,超时后将返回超时状态。

使用 Task.WaitAny 的基本语法

以下是 Task.WaitAny 的基本用法示例:

C#
using System.Threading.Tasks; Task[] tasks = new Task[3]; // 创建任务 int completedTaskIndex = Task.WaitAny(tasks); // 等待任意任务完成

示例:使用 Task.WaitAny 等待任务完成

以下示例展示了如何创建多个任务,并使用 Task.WaitAny 等待其中一个完成。

C#
namespace AppWaitAny { internal class Program { static void Main(string[] args) { Task[] tasks = new Task[3]; // 创建并启动多个任务 for (int i = 0; i < tasks.Length; i++) { int taskId = i + 1; tasks[i] = Task.Run(() => { int delay = new Random().Next(1000, 5000); // 随机延迟 Thread.Sleep(delay); Console.WriteLine($"任务 {taskId} 完成,耗时 {delay} 毫秒"); }); } // 等待任意任务完成 int completedTaskIndex = Task.WaitAny(tasks); Console.WriteLine($"任务 {completedTaskIndex + 1} 已完成。"); // 等待其他任务完成 Task.WaitAll(tasks); Console.WriteLine("所有任务已完成。"); } } }

image.png

2025-10-11
C#
00

ReaderWriterLockSlim 是 .NET Framework 中用于管理对共享资源的访问的一种同步机制。与传统的锁机制(如 MonitorMutex)相比,ReaderWriterLockSlim 更加高效,特别是在读操作远多于写操作的场景中。本文将详细介绍 ReaderWriterLockSlim 的特点、用法,以及多个示例以展示其应用。

ReaderWriterLockSlim 的特点

  • 读写分离:允许多个线程同时读取共享资源,但在写入时会独占访问。因此,它特别适合读多写少的场景。
  • 高效性:在读操作频繁的情况下,性能显著优于传统的锁,因为多个读线程可以并行执行。
  • 加锁机制:提供独占锁(用于写操作)、共享锁(用于读操作)机制,并支持升级锁定。
  • 优雅的管理:提供 EnterReadLockEnterWriteLockExitReadLockExitWriteLock 方法来控制访问。

使用 ReaderWriterLockSlim 的基本语法

以下是 ReaderWriterLockSlim 的基本用法示例:

C#
using System.Threading; ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(); // 读操作 rwLock.EnterReadLock(); // 读取共享资源 rwLock.ExitReadLock(); // 写操作 rwLock.EnterWriteLock(); // 写入共享资源 rwLock.ExitWriteLock();

3示例:使用 ReaderWriterLockSlim 实现线程安全的字典

以下示例展示了如何使用 ReaderWriterLockSlim 来实现一个线程安全的字典,支持多个线程同时读取,但是在写入时会进行独占访问。

C#
namespace AppReaderWriterLockSlim { internal class Program { private static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(); private static Dictionary<int, string> data = new Dictionary<int, string>(); static void Main(string[] args) { Thread[] writers = new Thread[3]; Thread[] readers = new Thread[5]; for (int i = 0; i < writers.Length; i++) { writers[i] = new Thread(Writer); writers[i].Start(i + 1); } for (int i = 0; i < readers.Length; i++) { readers[i] = new Thread(Reader); readers[i].Start(i + 1); } foreach (var writer in writers) { writer.Join(); } foreach (var reader in readers) { reader.Join(); } } static void Writer(object id) { for (int i = 0; i < 5; i++) { rwLock.EnterWriteLock(); try { int key = i + (int)id * 10; // 确保写入不同的键 data[key] = $"Writer {id} wrote {key}"; Console.WriteLine($"Writer {id} added key {key}"); } finally { rwLock.ExitWriteLock(); } Thread.Sleep(100); // 模拟延迟 } } static void Reader(object id) { for (int i = 0; i < 5; i++) { rwLock.EnterReadLock(); try { foreach (var kvp in data) { Console.WriteLine($"Reader {id} read: {kvp.Key} => {kvp.Value}"); } } finally { rwLock.ExitReadLock(); } Thread.Sleep(50); // 模拟延迟 } } } }

image.png

2025-10-11
C#
00

在C#的多线程编程中,Monitor 是一种用于同步多个线程访问共享资源的机制。它是基于对象的锁定机制,能够有效地控制对代码块的访问,防止数据的不一致,其实与lock基本一样的。本文将详细介绍 Monitor 的特点、用法,并提供多个示例以展示其应用。

Monitor 的特点

  • 独占性访问Monitor 通过锁定对象,确保同一时刻只有一个线程可以访问被锁定的代码块。
  • 高效性:相比于 MutexMonitor 的性能开销较小,适合在同一进程中的多线程环境中使用。
  • 支持条件变量Monitor 允许线程在等待某个条件时释放锁,这样其他线程可以获得锁,避免资源的浪费。
  • 易于使用Monitor 提供了较为简单的 APIs,如 EnterExitWaitPulsePulseAll

使用 Monitor 的基本语法

以下是 Monitor 的基本用法示例:

C#
object lockObject = new object(); Monitor.Enter(lockObject); // 请求锁 try { // ... 访问共享资源 } finally { Monitor.Exit(lockObject); // 释放锁 }

示例:使用 Monitor 实现线程安全的计数器

以下示例展示了如何使用 Monitor 来实现一个线程安全的计数器,确保只有一个线程可以对计数器进行更新。

C#
namespace AppMonitor01 { internal class Program { private static int counter = 0; // 共享资源 private static object lockObject = new object(); // 用于锁定代码块 static void Main(string[] args) { Thread[] threads = new Thread[5]; for (int i = 0; i < threads.Length; i++) { threads[i] = new Thread(IncrementCounter); threads[i].Start(); } foreach (var thread in threads) { thread.Join(); } Console.WriteLine($"最终计数器的值: {counter}"); } static void IncrementCounter() { for (int i = 0; i < 1000; i++) { Monitor.Enter(lockObject); // 请求锁 try { counter++; // 增加计数 } finally { Monitor.Exit(lockObject); // 确保释放锁 } } } } }

image.png

2025-10-11
C#
00

在C#的多线程编程中,Mutex 是一种用于同步多个线程的机制。它不仅适用于进程间的线程同步,还可以在同一进程内用于保护共享资源。本文将详细介绍 Mutex 的特点、用法,并提供多个示例以示范其应用。

Mutex 的特点

  • 跨进程同步Mutex 允许在不同进程之间进行同步,这使得它在处理需要进程间通信的场景时相当有用。
  • 独占性访问:只有一个线程可以获得 Mutex,其他线程必须等待,直到该线程释放 Mutex
  • 超时控制Mutex 允许设置超时。如果线程在指定时间内无法获得锁,可以选择放弃。
  • 简单易用Mutex 提供了清晰的接口,易于程序员使用。

使用 Mutex 的基本语法

以下是 Mutex 的基本用法示例:

C#
Mutex mutex = new Mutex(); mutex.WaitOne(); //请求锁 // ... 访问共享资源 mutex.ReleaseMutex(); //释放锁

示例:使用 Mutex 实现线程安全的计数器

以下示例展示了如何使用 Mutex 来实现一个线程安全的计数器,确保只有一个线程可以对计数器进行更新。

C#
using System; using System.Threading; class Program { private static int counter = 0; // 共享资源 private static Mutex mutex = new Mutex(); // 创建 Mutex 实例 static void Main(string[] args) { Thread[] threads = new Thread[5]; for (int i = 0; i < threads.Length; i++) { threads[i] = new Thread(IncrementCounter); threads[i].Start(); } foreach (var thread in threads) { thread.Join(); } Console.WriteLine($"最终计数器的值: {counter}"); } static void IncrementCounter() { for (int i = 0; i < 1000; i++) { mutex.WaitOne(); // 请求锁 try { counter++; // 增加计数 } finally { mutex.ReleaseMutex(); // 确保释放锁 } } } }

image.png

2025-10-11
C#
00

随着 .NET 6 的发布,LINQ 中新增了两个便捷的扩展方法:

  • MinBy(Func<T, TKey> keySelector)
  • MaxBy(Func<T, TKey> keySelector)

它们能根据指定的键(keySelector)从集合中一次性取得最小值或最大值所在的整个对象(而不仅仅是数值)。拥有这两个方法后,我们无需再手工使用 OrderBy() + First() 或者 OrderByDescending() + First() 的组合来取极值对象,代码变得更加简洁,也能避免没必要的排序操作,效率更高。

准备工作

在开始动手实验之前,我们先定义一个简单的 Student 记录类型,并创建一个学生列表,包含学生的姓名和分数等信息。示例如下:

C#
namespace AppMin { public record Student(string Name, int Marks); internal class Program { static void Main(string[] args) { // 创建学生列表 var students = new List<Student> { new Student("张三", 520), new Student("李四", 480), new Student("王五", 550), new Student("张飞", 430), new Student("关羽", 550) }; // 打印数据,确保数据已经正确初始化 foreach (var student in students) { Console.WriteLine($"{student.Name} - {student.Marks}"); } } } }