Calling a method in a UI thread from a lock ()

I have two methods, MethodA and MethodB . MethodB should work in the user interface thread. I need them to run one after another, not allowing MethodC to work between them.

MethodC is called when the user clicks on the cute little button.

What I did to make sure that it put Lock around the code this way:

  lock (MyLock) { MethodA(param1, param2); MyDelegate del = new MyDelegate(MethodB); if (this.IsHandleCreated) this.Invoke(del); } 

And for MethodC :

 public void MethodC() lock (MyLock) { Do bewildering stuff..... } 

The problem is that I'm stuck. It looks like my code is deadlocked.

When I look at the threads, I see that the code called by the button is stuck in lock (MyLock) in MethodC and my other thread seems to be stuck in this.Invoke(del) .

I read that it is dangerous to call a method from Lock but since I'm the one who wrote the code there, and this seems to be happening even with one Thread.Sleep I think this is not the code that Thread.Sleep me with.

Why did the Invoked method stop working? Is it possible to wait for methodC lock methodC before returning to the original lock from which it was called?

+4
source share
4 answers

So, imagine the following situation:

  • The code runs in the background thread. It captures the lock and then launches MethodA .

  • MethodC is called, and MethodA is in the middle of its work. MethodA expects the lock to be free, blocking the user interface thread until this happens.

  • The background thread ends with MethodA and proceeds to calling MethodB in the user interface thread. MethodB cannot work until all previous elements of the message queue are complete.

  • MethodC is at the top of the message queue, waiting for MethodB end, and MethodB is in the queue until MethodC ends. They are both waiting for each other, which is a dead end.

So how do you solve this problem? What you really need is some way to β€œwait” on the lock, without actually blocking the stream. Fortunately (in .NET 4.5) this is easy to do thanks to the parallel task library. (I'm waiting in quotation marks because we actually do not want to wait, we just want to execute MethodC as soon as the lock is released without actually waiting / blocking the current thread.)

Instead of using object for MyLock use:

 private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1); 

Now for MethodC you can do:

 public async Task MethodC() //you can change the signature to return `void` if this is an event handler { try { await semaphore.WaitAsync(); //Do stuff } finally { semaphore.Release(); } } 

The key point here is that since we are await when the semaphore is actually free, we are not blocking the current thread, which allows another background task to marshal MethodB on the MethodB thread, terminate the method, release the semaphore, and then enable this method.

Your other code is not needed (but it can still, if you want) use asynchronous wait on the semaphore; blocking the background thread is not almost the same problem, so the only key change is to use a semaphore instead of lock :

 public void Bar() { try { semaphore.Wait(); MethodA(param1, param2); MyDelegate del = new MyDelegate(MethodB); if (this.IsHandleCreated) this.Invoke(del); } finally { semaphore.Release(); } } 
+8
source

Assuming your MethodA also contains something like:

 lock(MyLock) { } 

You are absolutely right, you have a dead castle. MethodA cannot get a lock on MyLock because it was locked before the method was entered.

-1
source

You can try the following:

 Lock (MyLock) { MethodA(param1, param2); MyDelegate del = new MyDelegate(MethodB); MyDelegate del2 = new MyDelegate(MethodC); MyDelegate del3 = del+del2 if (this.IsHandleCreated) this.Invoke(del3); } 
-1
source

You have confused people using locks. This task has nothing to do with multi-threaded processing.

A simple usability solution is what you need - turn off your pretty little button until you're ready to launch MethodC.

-1
source

All Articles