在 C# 中,当使用 lock 关键字时,默认情况下其他线程如果遇到锁被占用会进入等待状态。若要让其他线程在锁被占用时不等待,可以通过以下几种方式实现:
- 使用 Monitor.TryEnter 方法
Monitor.TryEnter 方法尝试获取指定对象上的排他锁,若锁可用则获取锁并返回 true,若锁已被其他线程持有则立即返回 false,而不会让线程进入等待状态。
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
private static readonly object lockObject = new object();
static void Main()
{
// 创建多个任务来尝试获取锁
Task[] tasks = new Task[3];
for (int i = 0; i < 3; i++)
{
int taskId = i;
tasks[i] = Task.Run(() => TryAccessSharedResource(taskId));
}
// 等待所有任务完成
Task.WaitAll(tasks);
}
static void TryAccessSharedResource(int taskId)
{
if (Monitor.TryEnter(lockObject))
{
try
{
Console.WriteLine($"Task {taskId} acquired the lock and is accessing the shared resource.");
// 模拟一些耗时操作
Thread.Sleep(2000);
}
finally
{
Monitor.Exit(lockObject);
Console.WriteLine($"Task {taskId} released the lock.");
}
}
else
{
Console.WriteLine($"Task {taskId} could not acquire the lock and will not wait.");
}
}
}
代码解释
Monitor.TryEnter(lockObject) 尝试获取 lockObject 上的锁。
如果返回 true,则进入 try 块执行受保护的代码,完成后在 finally 块中调用 Monitor.Exit(lockObject) 释放锁。
如果返回 false,则执行 else 块中的代码,表明该线程未能获取锁且不会等待。
2. 使用 ReaderWriterLockSlim
ReaderWriterLockSlim 是一个更灵活的同步原语,它允许多个线程同时进行读操作,但在进行写操作时需要独占锁。可以使用 TryEnterWriteLock 或 TryEnterReadLock 方法来尝试获取锁,若锁不可用则立即返回 false。
示例代码
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
private static readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
static void Main()
{
// 创建多个任务来尝试获取写锁
Task[] tasks = new Task[3];
for (int i = 0; i < 3; i++)
{
int taskId = i;
tasks[i] = Task.Run(() => TryAccessWithWriteLock(taskId));
}
// 等待所有任务完成
Task.WaitAll(tasks);
}
static void TryAccessWithWriteLock(int taskId)
{
if (rwLock.TryEnterWriteLock(0))
{
try
{
Console.WriteLine($"Task {taskId} acquired the write lock and is accessing the shared resource.");
// 模拟一些耗时操作
Thread.Sleep(2000);
}
finally
{
rwLock.ExitWriteLock();
Console.WriteLine($"Task {taskId} released the write lock.");
}
}
else
{
Console.WriteLine($"Task {taskId} could not acquire the write lock and will not wait.");
}
}
}
代码解释
rwLock.TryEnterWriteLock(0) 尝试获取写锁,参数 0 表示不等待,若锁不可用则立即返回 false。
如果成功获取写锁,进入 try 块执行写操作,完成后在 finally 块中调用 rwLock.ExitWriteLock() 释放写锁。
如果未能获取写锁,执行 else 块中的代码,表明该线程不会等待
3. 使用 ConcurrentDictionary 等并发集合
对于一些常见的共享数据结构操作,如字典的读写操作,可以使用 ConcurrentDictionary 等并发集合,它们内部实现了线程安全机制,不需要手动加锁,并且在操作冲突时不会让线程进入等待状态。
示例代码
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
class Program
{
private static readonly ConcurrentDictionary<int, string> concurrentDict = new ConcurrentDictionary<int, string>();
static void Main()
{
// 创建多个任务来尝试添加元素到并发字典
Task[] tasks = new Task[3];
for (int i = 0; i < 3; i++)
{
int taskId = i;
tasks[i] = Task.Run(() => TryAddToConcurrentDictionary(taskId));
}
// 等待所有任务完成
Task.WaitAll(tasks);
}
static void TryAddToConcurrentDictionary(int taskId)
{
if (concurrentDict.TryAdd(taskId, $"Value {taskId}"))
{
Console.WriteLine($"Task {taskId} successfully added an element to the concurrent dictionary.");
}
else
{
Console.WriteLine($"Task {taskId} failed to add an element to the concurrent dictionary.");
}
}
}
代码解释
concurrentDict.TryAdd(taskId, $“Value {taskId}”) 尝试向 ConcurrentDictionary 中添加元素,若添加成功则返回 true,若键已存在则返回 false,不会让线程等待。
通过上述方法,可以实现让其他线程在锁被占用时不等待的需求。不同的场景可以选择合适的同步机制来提高程序的性能和响应能力。