Invoking Method on UI thread from within a Lock() - c#

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?

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

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.

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

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.

Related

Two Tasks run on the same thread which invalidates lock

Edit
I find Building Async Coordination Primitives, Part 1: AsyncManualResetEvent might be related to my topic.
In the case of TaskCompletionSource, that means that synchronous continuations can happen as part of a call to {Try}Set*, which means in our AsyncManualResetEvent example, those continuations could execute as part of the Set method. Depending on your needs (and whether callers of Set may be ok with a potentially longer-running Set call as all synchronous continuations execute), this may or may not be what you want.
Many thanks to all of the answers, thank you for your knowledge and patience!
Original Question
I know that Task.Run runs on a threadpool thread and threads can have re-entrancy. But I never knew that 2 tasks can run on the same thread when they are both alive!
My Question is: is that reasonable by design? Does that mean lock inside an async method is meaningless (or say, lock cannot be trusted in async method block, if I'd like a method that doesn't allow reentrancy)?
Code:
namespace TaskHijacking
{
class Program
{
static TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
static object methodLock = new object();
static void MethodNotAllowReetrance(string callerName)
{
lock(methodLock)
{
Console.WriteLine($"Enter MethodNotAllowReetrance, caller: {callerName}, on thread: {Thread.CurrentThread.ManagedThreadId}");
if (callerName == "task1")
{
tcs.SetException(new Exception("Terminate tcs"));
}
Thread.Sleep(1000);
Console.WriteLine($"Exit MethodNotAllowReetrance, caller: {callerName}, on thread: {Thread.CurrentThread.ManagedThreadId}");
}
}
static void Main(string[] args)
{
var task1 = Task.Run(async () =>
{
await Task.Delay(1000);
MethodNotAllowReetrance("task1");
});
var task2 = Task.Run(async () =>
{
try
{
await tcs.Task; // await here until task SetException on tcs
}
catch
{
// Omit the exception
}
MethodNotAllowReetrance("task2");
});
Task.WaitAll(task1, task2);
Console.ReadKey();
}
}
}
Output:
Enter MethodNotAllowReetrance, caller: task1, on thread: 6
Enter MethodNotAllowReetrance, caller: task2, on thread: 6
Exit MethodNotAllowReetrance, caller: task2, on thread: 6
Exit MethodNotAllowReetrance, caller: task1, on thread: 6
The control flow of the thread 6 is shown in the figure:
You already have several solutions. I just want to describe the problem a bit more. There are several factors at play here that combine to cause the observed re-entrancy.
First, lock is re-entrant. lock is strictly about mutual exclusion of threads, which is not the same as mutual exclusion of code. I think re-entrant locks are a bad idea in the 99% case (as described on my blog), since developers generally want mutual exclusion of code and not threads. SemaphoreSlim, since it is not re-entrant, mutually excludes code. IMO re-entrant locks are a holdover from decades ago, when they were introduced as an OS concept, and the OS is just concerned about managing threads.
Next, TaskCompletionSource<T> by default invokes task continuations synchronously.
Also, await will schedule its method continuation as a synchronous task continuation (as described on my blog).
Task continuations will sometimes run asynchronously even if scheduled synchronously, but in this scenario they will run synchronously. The context captured by await is the thread pool context, and the completing thread (the one calling TCS.TrySet*) is a thread pool thread, and in that case the continuation will almost always run synchronously.
So, you end up with a thread that takes a lock, completes a TCS, thus executing the continuations of that task, which includes continuing another method, which is then able to take that same lock.
To repeat the existing solutions in other answers, to solve this you need to break that chain at some point:
(OK) Use a non-reentrant lock. SemaphoreSlim.WaitAsync will still execute the continuations while holding the lock (not a good idea), but since SemaphoreSlim isn't re-entrant, the method continuation will (asynchronously) wait for the lock to be available.
(Best) Use TaskCompletionSource.RunContinuationsAsynchronously, which will force task continuations onto a (different) thread pool thread. This is a better solution because your code is no longer invoking arbitrary code while holding a lock (i.e., the task continuations).
You can also break the chain by using a non-thread-pool context for the method awaiting the TCS. E.g., if that method had to resume on a UI thread, then it could not be run synchronously from a thread pool thread.
From a broader perspective, if you're mixing locks and TaskCompletionSource instances, it sounds like you may be building (or may need) an asynchronous coordination primitive. I have an open-source library that implements a bunch of them, if that helps.
A task is an abstraction over some amount of work. Usually this means that the work is split into parts, where the execution can be paused and resumed between parts. When resuming it may very well run on another thread. But the pausing/resuming may only be done at the await statements. Notably, while the task is 'paused', for example because it is waiting for IO, it does not consume any thread at all, it will only use a thread while it is actually running.
My Question is: is that reasonable by design? Does that mean lock inside an async method is meaningless?
locks inside a async method is far from meaningless since it allows you to ensure a section of code is only run by one thread at a time.
In your first example there can be only one thread that has the lock at a time. While the lock is held that task cannot be paused/resumed since await is not legal while in a lock body. So a single thread will execute the entire lock body, and that thread cannot do anything else until it completes the lock body. So there is no risk of re-entrancy unless you invoke some code that can call back to the same method.
In your updated example the problem occurs due to TaskCompletionSource.SetException, this is allowed to reuse the current thread to run any continuation of the task immediately. To avoid this, and many other issues, make sure you only hold the lock while running a limited amount of code. Any method calls that may run arbitrary code risks causing deadlocks, reentrancy, and many other problems.
You can solve the specific problem by using a ManualResetEvent(Slim) to do the signaling between threads instead of using a TaskCompletionSource.
So your method is basically like this:
static void MethodNotAllowReetrance()
{
lock (methodLock) tcs.SetResult();
}
...and the tcs.Task has a continuation attached that invokes the MethodNotAllowReetrance. What happens then is the same thing that would happen if your method was like this instead:
static void MethodNotAllowReetrance()
{
lock (methodLock) MethodNotAllowReetrance();
}
The moral lesson is that you must be very careful every time you invoke any method inside a lock-protected region. In this particular case you have a couple of options:
Don't complete the TaskCompletionSource while holding the lock. Defer its completion until after you have exited the protected region:
static void MethodNotAllowReetrance()
{
bool doComplete = false;
lock (methodLock) doComplete = true;
if (doComplete) tcs.SetResult();
}
Configure the TaskCompletionSource so that it invokes its continuations asynchronously, by passing the TaskCreationOptions.RunContinuationsAsynchronously in its constructor. This is an option that you don't have very often. For example when you cancel a CancellationTokenSource, you don't have the option to invoke asynchronously the callbacks registered to its associated CancellationToken.
Refactor the MethodNotAllowReetrance method in a way that it can handle reentrancy.
Use SemaphoreSlim instead of lock, since, as the documentation says:
The SemaphoreSlim class doesn't enforce thread or task identity
In your case, it would look something like this:
// Semaphore only allows one request to enter at a time
private static readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
void SyncMethod() {
_semaphoreSlim.Wait();
try {
// Do some sync work
} finally {
_semaphoreSlim.Release();
}
}
The try/finally block is optional, but it makes sure that the semaphore is released even if an exception is thrown somewhere in your code.
Note that SemaphoreSlim also has a WaitAsync() method, if you want to wait asynchronously to enter the semaphore.

AutoResetEvent.WaitOne() cause deadlock

I'm writing a application with a critical region.
And I decide to use AutoResetEvent to achieve mutual exclusion.
Here's the code
public class MyViewModel
{
private AutoResetEvent lock = new AutoResetEvent(true);
private aync Task CriticalRegion()
{
Dosomething();
}
public async Task Button_Click()
{
Debug.WriteLine("Entering Button_Click");
lock.WaitOne();
try
{
await CriticalRegion();
}
finally
{
lock.Set();
Debug.WriteLine("Leaving Button_Click");
}
}
}
I have a button whose click event calls the Button_Click() method
It works normally. But, if I'm quick enough to click the button for another time before the first call to Button_Click() completes, the whole app stops responding.
In the Debug window I find something like this
Entering Button_Click
Entering Button_Click
Looks like the method never completes.
I struggled a bit and find that if I change lock.WaitOne(); to
if (!sync.WaitOne(TimeSpan.FromSeconds(1)))
{
return;
}
In this case my app is able to avoid the deadlock,but I don't know why it works.
I only know about the IPC from my OS course and the async and await pattern in C#, and I'm not so familiar with the thread in .Net world.
I really want to understand what's really going on behind the scenes.
Thanks for any replys ;)
You have a deadlock because WaitOne is blocking the main thread (button click handler is executed on the main thread), while you haven't called ConfigureAwait(false) when calling await, which means that it tries to run the code which is after await on the main thread, even if it's blocked, which would causes a deadlock.
I suggest reading this post for a thorougher explanation of the dead lock situation.
For your code, I would suggest putting the lock deeper, probably within the async Task, and trying to use a more suitable pattern for locking, preferably, the lock statement, because using Event objects is awkward for mutual exclusion, as Hans stated in the comment.
AutoResetEvent.WaitOne() will block infinitely until you call AutoResetEvent.Set(), which you never seem to do except for after the WaitOne() call.
Quoting the AutoResetEvent.WaitOne() documentation:
Blocks the current thread until the current WaitHandle receives a signal.

Why UI in Winforms can be updated inside while loop with async operation?

I am new in Async/Await operation. So after i read docs and research about it with practise. I got this which i do not know how it work behind the scene.
This code free UI obviously:
private async void button1_Click(object sender, EventArgs e)
{
while (true)
{
label1.Text = DateTime.Now.ToString(CultureInfo.InvariantCulture); // ui thread - sync
}
}
But this is not free UI:
private async void button1_Click(object sender, EventArgs e)
{
while (true)
{
await Task.Delay(1000); // async
label1.Text = DateTime.Now.ToString(CultureInfo.InvariantCulture); // ui thread - sync
}
}
In my mind after Task.Delay finished, remaining code will run on UI thread but while loop stop rendering UI. I am stuck at this point !
My question is why UI can be updated if i use async method inside?
Please ask me more if question not clearly. Thank you.
Quite often people think that when async-await is used the job is done by separate threads. In fact all work is done by one thread. This thread will do all normal work until it sees an await. Instead of really waiting for the function to complete it goes up its call stack to see it the caller has something to do ( = to see if the caller is not awaiting), it performs the statements until the caller has to await. The thread goes up in the call stack to see if it can do something else until an await, etc. Once the complete call stack is awaiting it checks if the original await is already finished. If so the thread continues with the statements after the await, if not the thread comes to a halt until the task is really finished.
So as long as somewhere in the thread there is no await, your thread can do other things instead of waiting.
private async Task A()
{
Do1();
var taskB = B(); // call B without await
Do2();
await taskB; // wait for B to finish
Do3();
}
private async Task B()
{
Do4();
var taskC = C(); // call C without await
Do5();
await taskC; // wait for C to finish
Do6();
}
private async Task C()
{
Do7();
await Task.Delay(TimeSpan.FromSeconds(0.5));
Do8();
}
Once A() is called, you'll see that the following are called: Do1; B; Do4; C; Do7; Task.Delay, where the thread meets the first await.
Because of async-await the thread doesn't wait in C for Task.Delay.
Instead the thread goes up its call stack to see if it can do something in B. In the example, B is not awaiting yet, so Do5 is called. Now we meet the await in B. The thread still doesn't wait, but goes up in its call stack to procedure A to see if it can do something in A. And indeed, because A is not awaiting after calling B, Do2 can be called. Then A starts awaiting. The thread goes up in its call stack to see ..., etc.
Once everyone in the call stack is awaiting the thread goes back to Task.Delay and really starts waiting until the time elapses. After the await the thread knows that everyone in the call stack is awaiting.
The thread continues with Do8 and goes up its call stack to continue after the first await after the call to C. (after all it knows that everyone was awaiting). There Do6 is called, and the thread returns to the statement after the await in A: Do3.
So you see that async-await influences the order in which procedures are called. Therefore you need to await for any procedure where you need the results from.
So if in the example Do2 needs the result of anything happening in B() (and thus in Do4/Do5/Do6/C/Do7/Do8), we should await B() before calling Do2.
Note that it looks as if several things are performed simultaneously, but in fact they are all done separately by the same thread.
Eric Lippert compared this behaviour with a cook in a restaurant: Eric Lippert about async-await. Search somewhere in the middle for async await: if the cook toasts bread he doesn't wait until the bread is toasted, but starts boiling water to cook the eggs. The cook will not put bread in the toaster and water in the sauce pan at the same time, but instead of waiting for one thing he'll start doing the other things where he doesn't need the toasted bread.
Another link for async-await that helped me a lot to understand it: Stephen Cleary about async and await
Now back to your question.
In your first example you wrote: This code free UI obviously:
I assume you mean: This code freezes the UI obviously. And indeed it does.
In your 2nd example the thread doesn't start doing nothing when it awaits Task.Delay. Instead it goes up its call steck to see if it can do something else, like reacting on mouse clicks. That's how it keeps the UI alive.
When the code hits the await, the code will enqueue a continuation on the Delay task and then leave the UI thread. That means for the next second, the UI thread is free to do any rendering work it needs.
After the one second elapses, the Task completes, the continuation is executed on the UI thread (thanks to a SynchronizationContext) and the cycle repeats.

Can this cause deadlock? BeginInvoke() & thread.Join()

I have this code that many threads can call to update the GUI:
MethodInvoker del = () => { lblInfo.Text = tmp; };
lblInfo.BeginInvoke(del);
(lblInfo is created by the GUI thread)
I also have this method called at button click executed by the GUI thread:
public void Stop()
{
isStopping = true;
crawler.Join();
foreach (Thread t in txtWorkers)
{
t.Join();
}
indexer.Join();
lblStatus.Text = "Stopped";
lblInfo.Text = "";
}
1 time over 100 run the program deadlock at Stop button click. I was not debugging when i saw the deadlock so i can't be sure about the state of the various threads but i'm almost sure that all the threads i'm joining will eventually reach the point where they check for
isStopping value and terminate. This leads me to think that there may be a problem with the BeginInvoke but can't really find it. It should be async so threads calling it (crawler & indexer) should not block. What happens if the GUI thread is executing Stop() and also must execute a call from BeginInvoke? Could this be the problem? Is there something i can't see about the thread i'm joining?
EDIT:
What the code looks like after the suggested changes:
public void Stop()
{
/*
...disable GUI
*/
isStopping = true; // Declared as volatile
lblStatus.Text = "Stopping...";
// Creating a thread that will wait for other threads to terminate
Task.Factory.StartNew(() =>
{
crawler.Join();
foreach (Thread t in txtWorkers)
{
t.Join();
}
indexer.Join();
// Adjust UI now that all threads are terminated
MethodInvoker del = () =>
{
/*
...enable GUI
*/
lblStatus.Text = "Not Running";
isStopping = false;
};
lblStatus.BeginInvoke(del);
});
}
It seems to be working, i hope that deadlock is gone...
I don't think it should be a problem, because you're using BeginInvoke rather than Invoke - the background threads will just proceed past that line without waiting for the GUI to catch up. If you're using Control.Invoke anywhere, that could cause a deadlock.
More importantly, using Join in your GUI thread is fundamentally a bad idea - the UI will be frozen until everything's finished. It would be better to disable any controls which could start anything new, set your isStopping flag, and then create a new thread to wait for all the threads to stop - and when all the threads have finished, then update the UI with BeginInvoke again. (If you're using .NET 4.5 you could also use an asynchronous method for this, creating and awaiting a task to wait for all the threads.)
Finally, if isStopping is just a bool field, there's no guarantee that your background threads will "see" the change from the UI thread. It's possible that making the field volatile would fix this, but the precise meaning of volatile scares me. An alternative would be to use the Interlocked class, or make it a property which obtains a lock for both reading and writing - that ensures appropriate memory barriers are in place.

referencing and closing thread instantly

I have the following code:
public class GUI
{
public void threadTask()
{
while(MY_GLOBAL_VARIABLE)
{
// do something
}
}
}
// Execute Thread
GUI gui = new GUI();
Thread t = new Thread(threadTask);
t.Start();
This seems like a messy way to do this. Any better approach how to reference it and kill it instantly? Thanks!
UPDATE: who ever gave me a downvote, LEARN TO READ! I clearly specified what I'm trying to 'kill' in the title, tags and code, at least next time read the post before casting a vote.
What you have is fine, the thread will be cleaned up as soon as it's finished processing.
You don't kill/dispose a thread, it has no IDisposable.
You could put the thread start in a method, so you can call it more then once, when it finished the first time.
You can use:
t.Join();
to catch when the thread finished.
The call to Join() is what de-allocates the thread. You don't have to do anything else. Just make sure that the threads clean up any resources they might be using before they exit.

Categories

Resources