如何实现线程安全列表
本文关键字:线程 安全 列表 实现 何实现 | 更新日期: 2023-09-27 18:17:45
我想实现一个线程安全列表,但是线程安全必须在整个操作块上得到保证,而不仅仅是在单个操作上(例如:添加,删除等)用例应该如下所示:
list.Lock();
list.Add(sth);
list.RemoveAt(4);
list.Unlock();
我希望列表对任何操作都需要锁。例如:
list.Add(sth);
在没有预先锁定的情况下调用会导致异常。这就是为什么我不使用lock()
语句——锁检查对于这个解决方案是至关重要的。
使用Monitor实现这样的列表并不难,但只有在想要检查列表是否被锁定时才会这样做。我想到了下面的场景:
// Inside list class
private object lockObject;
private bool locked;
public void Lock()
{
Monitor.Enter(lockObject);
locked = true;
}
public void Unlock()
{
Monitor.Exit(lockObject);
locked = false;
}
不幸的是,无论locked
是在进入或离开临界区之前还是之后设置的,这段代码都容易出现竞态条件。
另一种方法是使用TryEnter,但是如果没有获得锁,这个方法实际上会进入临界区,这也可能导致竞争条件。
我应该如何实现这个机制是线程安全的,如何避免竞争条件,而检查,列表是否被锁定?
你可以让你的类负责锁定,并且只公开一个接受消费者指定的Action
的方法:
public class LockedCollection
{
private class CollectionImpl : ICollection<int>
{
//Collection methods
}
private sealed class CollectionWrapper : ICollection<int>, IDisposable
{
public CollectionWrapper(CollectionImpl inner)
{
_inner = inner;
}
private CollectionImpl _inner
//Collection methods all just wrapping calls to inner
public void Dispose()
{
_inner = null;
}
}
private CollectionImpl _instance - new CollectionImpl();
private object _lock = new object();
public void DoStuff(Action<ICollection<int>> task)
{
lock(_lock)
{
using(var wrapper = new CollectionWrapper(_instance))
{
task(wrapper);
}
}
}
}
消费者可以在task
中进行他们喜欢的任何操作序列,并且您知道锁已被占用-因为您自己获取了它。
我正在考虑一些类似于Builder模式的东西。
使用例子:
list
.do() // mandatory initial statement.
// Doesn't acquire a lock - it just builds a transaction object
.add(42) // Add an operation to the transaction object.
// Call would be illegal without performing do() before
.removeAt(0) // Another added operation
.end(); // Acquire the lock, perform the specified changes, release the lock.
end()
执行的锁获取可以是对sync {...}
的简单调用——不存在竞争条件
如果为用作锁的对象提供getter,则可以使用标准的锁定机制。虽然我不认为这是一个优雅的解决方案,但它肯定会达到目的。
void performOpertations(TSList list) {
lock(list.getLock()) {
list.add(obj);
list.remove(obj2);
// do your stuff
}
}
由于c#同步块是可重入的,你将能够调用列表的内部同步方法,如示例中的add()
或remove()
。
另一个解决方案是使用访问者模式,并让访问者操作被同步块包围。
我没有理由不使用ReaderWriterLockSlim:
class MyThreadSafeList<T>
{
private readonly List<T> internalList = new List<T>();
private readonly ReaderWriterLockSlim lockSlim = new ReaderWriterLockSlim();
public void EnterReadLock()
{
lockSlim.EnterReadLock();
}
public void ExitReadLock()
{
lockSlim.ExitReadLock();
}
public void EnterWriteLock()
{
lockSlim.EnterWriteLock();
}
public void ExitWriteLock()
{
lockSlim.ExitWriteLock();
}
public void Add(T item)
{
if (!lockSlim.IsWriteLockHeld)
{
throw new InvalidOperationException();
}
internalList.Add(item);
}
public void Remove(T item)
{
if (!lockSlim.IsWriteLockHeld)
{
throw new InvalidOperationException();
}
internalList.Remove(item);
}
public T this[int index]
{
get
{
if (!lockSlim.IsReadLockHeld)
{
throw new InvalidOperationException();
}
return internalList[index];
}
set
{
if (!lockSlim.IsWriteLockHeld)
{
throw new InvalidOperationException();
}
internalList[index] = value;
}
}
}