Reputation: 944
I have two methods, MethodA
& MethodB
. MethodB
has to run on the UI thread. I need them to run one after the other without allowing MethodC
to run between them.
MethodC
is called when a user clicks on a lovely little button.
What I did to ensure this is put a Lock
around the code thus:
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.....
}
Problem is I'm getting stuck. It looks like my code's going into a deadlock.
When I look at the threads I see that the code called by the button click is stuck at lock (MyLock)
in MethodC
and my other thread seems stuck at this.Invoke(del)
.
I've read it's dangerous to invoke a method from within a Lock
but since I'm the one who wrote the code there and this seems to happen even with just a Thread.Sleep
I figure it's not the code that's getting me into trouble.
Why would the the Invoked method stop working?
Is it possibly waiting for the lock on methodC
to be released before going back to the original lock it was invoked from?
Upvotes: 5
Views: 3473
Reputation: 10463
You confused a hell out of people using lock. This task has nothing to do with multi-threading per se.
Simple usability solution is what you need - disable your lovely little button until you are ready to run MethodC.
Upvotes: -1
Reputation: 203829
So, imagine the following situation:
Your background thread starts running the code. It grabs the lock and then starts running MethodA
.
MethodC
is called while MethodA
is in the middle of its work. MethodA
waits for the lock to be free, blocking the UI thread until that happens.
The background thread finishes MethodA
and goes to invoke MethodB
on the UI thread. MethodB
can't run until all previous items in the message pump's queue have finished.
MethodC
is at the top of the message pump's queue, waiting until MethodB
finishes, and MethodB
is in the queue waiting until MethodC
finishes. They are both waiting on each other, which is a deadlock.
So, how do you resolve this issue? What you really need is some way of "waiting" on a lock without actually blocking the thread. Fortunately (in .NET 4.5) this is easy to do thanks to the Task Parallel Library. (I have waiting in quotes because we don't actually want to wait, we just want to execute MethodC
as soon as the lock is freed without actually waiting/blocking the current thread.)
Instead of using an 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 here is that because we await
a task that represents when the semaphore is actually free we aren't blocking the current thread, which will allow the other background task to marshal MethodB
to the UI thread, finish the method, release the semaphore, and then let this method execute.
Your other code doesn't need to (but still can, if you want) use async waiting on the semaphore; blocking the background thread isn't nearly as much of a problem, so the only key change there is using the 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();
}
}
Upvotes: 10
Reputation: 2962
You can rather try this:
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);
}
Upvotes: -1
Reputation: 19842
Assuming your MethodA
also contains something like:
lock(MyLock) {
}
You are absolutely correct, you've got a dead lock. MethodA
cannot obtain a lock on MyLock
because it was already locked before the method was entered.
Upvotes: -1