多线程和锁定(线程安全操作)
本文关键字:安全 操作 线程 锁定 多线程 | 更新日期: 2023-09-27 18:00:59
所以我有一个包含一些方法的类,这些方法都使用锁定,以防止当有人使用我的类的实例并有多个线程访问它时发生奇怪的事情:
public class SomeRandomClass
{
private object locker = new object();
public void MethodA()
{
lock (locker)
{
// Does something
MethodB();
}
}
public void MethodB()
{
lock (locker)
{
// Does something else
}
}
}
如我们所见,MethodB()
自动访问 MethodA()
,但这不起作用,因为MethodA()
目前已锁定了储物柜对象。
我想让MethodB()
公开访问,所以你可以在需要时手动调用它,但我不希望它在MethodA()
做事时被使用(这就是我使用储物柜对象的原因(。
当然,我不希望MethodA()
在MethodB()
做事的时候做事。我基本上只希望同时使用所有方法中的一种,但MethodA()
需要以某种方式访问MethodB()
而无需删除锁(以便它始终保持完全线程安全(。
我真的希望我想问的是可以理解的......如果对我的问题有任何疑问,请继续并在下面发布。答案/解决方案也非常感谢!
解决方案可能非常简单,我只是没有看到它。
顺便说一下,上面应该是 C# 代码。
解决方案是创建一个私有方法,其中包含MethodB
可以由MethodA
和另一个公共MethodB
调用的内容
私人MethodB
不会锁定,只有公共会锁定。
例如:
public class SomeRandomClass {
private object locker = new object();
public void MethodA {
lock(locker) {
// exclusive club
// do something before calling _methodB
_methodB();
}
}
private void _methodB {
// do that, what used to be done by MethodB
}
public void MethodB {
//this one only exists to expose _methodB in a thread-safe context
lock(locker) {
_methodB();
}
}
}
附言
我认为对于您和其他所有人来说,为什么您的代码在某种程度上旨在创建死锁是显而易见的。
更新:
显然,正如评论中指出的那样,lock(object) {}
是可重入的,因此明显的僵局甚至不是一个。
锁定禁止你尝试做的事情——这就是它的目的。
这里要做的一件事是创建一个可以从methodA
和methodB
访问的私有方法。该方法不会使用锁定,并且不是线程安全的,但可以从任一锁定方法调用。
这里有竞争条件:它使数据不正确。我想方法 A 写入字符串类型的静态theVar
变量:
thread A -> call method A -> lock -> change theVar to "A"
thread B -> call method B -> wait because thread A keep lock
thread A -> release lock to call method B
The bug here: thread B process theVar of "A"
If method B only read theVar, it's Ok.
您的锁定机制需要允许以递归方式(仅通过同一线程(获取锁,通常称为 reentrant
。 lock
(内部监视类(。
同一线程多次调用 Enter 而不阻塞是合法的;但是,在等待对象的其他线程取消阻塞之前,必须调用相同数量的 Exit 调用。
另请参阅使用 lock 语句在 C# 中递归/嵌套锁定和 C# 中的重入锁
正如Henk Holterman在评论中指出的那样,Monitor
类已经可以重入。lock
语句是管理对基础Monitor
类的适量的Enter
和Exit
调用。
ReaderWriterLockSlim
类是锁定机制的一个例子,可以在reentrant
和non-reentrant
之间进行选择。请参阅 https://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim(v=vs.110(.aspx
var rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
将您的lock { ... }
替换为
ReaderWriterLockSlim rwLock =
new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
...
try
{
rwLock.EnterWriteLock();
// Does something
}
finally
{
rwLock.ExitWriteLock();
}
'''
你写的代码是正确的。
因为根据Microsoft的说法,一旦获取了调用,即使程序在同一流中调用锁定,它也不会被阻塞,因为锁已经与线程在一起。代码的工作原理如下。
- 调用 "MethodA" -->获取锁 -
->调用 "MethodB"(不会被阻止,因为线程已被获取锁(,执行将完成。
在从另一个线程的先前执行之间调用"MethodB",执行将被阻止,因为锁定是第一个线程。