multithreading和锁定(线程安全操作)
所以我有一个类,有一些方法都使用锁定,以防止有人使用我的类的实例与多个线程访问它时发生奇怪的事情:
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()
当前已锁定了locker对象,因此无法使用。
我想公开访问MethodB()
,因此您可以在需要时手动调用它,但我不希望在MethodA()
使用它(这就是我使用锁定器对象的原因)。
当然,当MethodB()
正在做的事情时,我不希望MethodA()
做事情。 我基本上只希望同时使用所有方法中的一个,但MethodA()
需要以某种方式访问MethodB()
而不删除锁(以便它始终保持完全线程安全)。
我真的希望我能提出的问题是可以理解的……如果对我的问题有任何疑问,请继续将其发布在下面。 答案/解决方案也非常感谢!
解决方案可能非常简单,我只是没有看到它。
顺便说一下,上面应该是C#-code。
一个简单的解决方案是创建一个私有方法,该方法包含MethodB
和MethodA
以及另一个公共 MethodB
可以调用的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(); } } }
PS
我认为对于您和其他所有人来说,为什么您的代码在某种程度上旨在创建死锁是显而易见的。
更新 :
显然, lock(object) {}
是可重入的,如注释中所指出的,因此明显的死锁甚至不是一个。
锁定禁止你正在尝试做什么 – 这就是它的目的。
这里要做的一件事是创建一个可以从methodB
和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调用。
另请参见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 { ... }
替换lock { ... }
ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); ... try { rwLock.EnterWriteLock(); // Does something } finally { rwLock.ExitWriteLock(); }
“`
您编写的代码是正确的。
因为根据微软的说法,即使程序在同一个流程中要求锁定,一旦获得调用,它就不会被锁定,因为锁定已经在线程中。 代码如下。
-
调用“MethodA” – >获取锁定 – >调用“MethodB”(不会因为线程已被获取而被阻止锁定)并执行完毕。
-
在之前从另一个线程执行之间调用“MethodB”,执行将被锁定,因为lock是第一个线程。