在现代 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("所有任务已完成。");
}
}
}

ReaderWriterLockSlim 是 .NET Framework 中用于管理对共享资源的访问的一种同步机制。与传统的锁机制(如 Monitor 和 Mutex)相比,ReaderWriterLockSlim 更加高效,特别是在读操作远多于写操作的场景中。本文将详细介绍 ReaderWriterLockSlim 的特点、用法,以及多个示例以展示其应用。
ReaderWriterLockSlim 的特点EnterReadLock、EnterWriteLock、ExitReadLock 和 ExitWriteLock 方法来控制访问。ReaderWriterLockSlim 的基本语法以下是 ReaderWriterLockSlim 的基本用法示例:
C#using System.Threading;
ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
// 读操作
rwLock.EnterReadLock();
// 读取共享资源
rwLock.ExitReadLock();
// 写操作
rwLock.EnterWriteLock();
// 写入共享资源
rwLock.ExitWriteLock();
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); // 模拟延迟
}
}
}
}

在C#的多线程编程中,Monitor 是一种用于同步多个线程访问共享资源的机制。它是基于对象的锁定机制,能够有效地控制对代码块的访问,防止数据的不一致,其实与lock基本一样的。本文将详细介绍 Monitor 的特点、用法,并提供多个示例以展示其应用。
Monitor 的特点Monitor 通过锁定对象,确保同一时刻只有一个线程可以访问被锁定的代码块。Mutex,Monitor 的性能开销较小,适合在同一进程中的多线程环境中使用。Monitor 允许线程在等待某个条件时释放锁,这样其他线程可以获得锁,避免资源的浪费。Monitor 提供了较为简单的 APIs,如 Enter、Exit、Wait、Pulse 和 PulseAll。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); // 确保释放锁
}
}
}
}
}

在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(); // 确保释放锁
}
}
}
}

随着 .NET 6 的发布,LINQ 中新增了两个便捷的扩展方法:
它们能根据指定的键(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}");
}
}
}
}