Multithreading and Locking (Thread-Safe operations)

☆樱花仙子☆ 提交于 2019-12-06 16:06:10

An easy solution would be to create a private method that contains what MethodB does that can be called by MethodA and another public MethodB

The private MethodB does not lock, only the public ones do.

For example:

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();
        }
    }
}

P.S.

I think it is obvious to you and everyone else why your code is somewhat designed to create a deadlock.


Update:

Apparently lock(object) {} is re-entrant as pointed out in the comments, so the obvious deadlock isn't even one.

Locking forbids what you're trying to do -- that's its purpose.

One thing to do here is creating a private method that you can access from both methodA and methodB. That method wouldn't use locking, and wouldn't be thread safe, but could be called from either one of the locking methods.

You have race condition here: it make data incorrect. I suppose method A write static theVar variable of type string:

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.
Muraad Nofal

Your lock mechanism needs to allow locks to be taken in a recursive way (by the same thread only), usually called reentrant. lock (Monitor class internally).

It is legal for the same thread to invoke Enter more than once without it blocking; however, an equal number of Exit calls must be invoked before other threads waiting on the object will unblock.

See also Recursive / nested locking in C# with the lock statement and Re-entrant locks in C#

As pointed out by Henk Holterman in the comment, the Monitor class is already reentrant. And the lock statement is managing the right amount of Enter and Exit calls to the underlying Monitor class.

The ReaderWriterLockSlim class is an example for a lock mechanism where one can choose between reentrant and non-reentrant. See https://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim(v=vs.110).aspx

var rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

Replace your lock { ... } with

ReaderWriterLockSlim rwLock = 
    new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

...

try 
{
    rwLock.EnterWriteLock();
    // Does something
}    
finally 
{
    rwLock.ExitWriteLock();
}

```

The Code written by you is correct.

Because according to Microsoft, once the call is acquired even if program calls for lock in the same flow, it will not be blocked as lock is already with the thread. The code works as below.

  1. call "MethodA" -->acquire lock --> call "MethodB" (will not be blocked as thread is already acquired lock) and execution will be completed.

  2. Call "MethodB" in between previous execution from another thread, the execution will be blocked as lock is with first thread.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!