Is it Safe to use ReaderWriterLockSlim in an async method - c#

Since the ReaderWriterLockSlim class uses Thread ID to see who owns the lock is it safe to use with async methods where there is no guarantee that all the method will be executed on the same thread.
For example.
System.Threading.ReaderWriterLockSlim readerwriterlock = new System.Threading.ReaderWriterLockSlim();
private async Task Test()
{
readerwriterlock.EnterWriteLock();
await Task.Yield(); //do work that could yield the task
readerwriterlock.ExitWriteLock(); //potentailly exit the lock on a different thread
}

Is it Safe to use ReaderWriterLockSlim in an async method
Yes and no. It can be safe to use this in an async method, but it is likely not safe to use it in an async method where you enter and exit the lock spanning an await.
In this case, no, this is not necessarily safe.
ExitWriteLock must be called from the same thread that called EnterWriteLock. Otherwise, it throws a SynchronizationLockException. From the documentation, this exception is thrown when:
The current thread has not entered the lock in write mode.
The only time this would be safe is if this was used in an async method which was always in an environment where there was a current SynchronizationContext in place which will move things back to the same thread (ie: Windows Forms, WPF, etc), and wasn't used by a nested async call where a "parent" up the call chain setup a Task with ConfigureAwait(false) (which would prevent the Task from capturing the synchronization context). If you are in that specific scenario, you'd know the thread would be maintained, as the await call would marshal you back onto the calling context.

No. Thread-affine coordination primitives should not be used as in your example.
You correctly identified the problem where a different thread can be used to resume after the await. There is another issue due to the the way async methods return early: the caller is not aware that the lock is held.
ReaderWriterLockSlim by default is a non-recursive lock, so if another async method attempts to take the same lock, you'll get a deadlock. Even if you make the lock recursive, you'll still end up with a problem: arbitrary end-user code should never be called while holding a lock, and that's essentially what you're doing when you use an await.
The SemaphoreSlim type is async-aware (via its WaitAsync method), and Stephen Toub has a series of async coordination primitives also available in my AsyncEx library.

Related

which is the difference between task.whenAll and configurewait?

I have an async method that get data from a database:
private async Task getDataAsync()
{
await getDataFromDatabaseAsync();
}
I have a contructor that uses this method, in this way:
public MyViewModel()
{
Task.WhenAll(getDataAsync());
//getDataAsync().ConfigureAwait(false);
//next line of code
}
The only way that I get to avoid the applications isn't blocked is using one the two options, both seems to work fine. I can use WhenAll or I can use ConfigureAwait(false).
Another options it is calling the method getDataASync() inside a task, but I guess it is a worse option becase it takes more resources.
So I would like to know which is the differences between WhenAll and ConfigureAwait.
When I use WhenAll in this way, the line of code "next line of code" is run after the async method is finisihed or it will run before finish?
Thanks.
There is a common misconception that when you make a method async, it will be actually executed asynchronously (i.e. on a separate thread). That is not the case: async and await are a means to synchronize already asnychronous code. If you have nothing that is executed on a separate thread, your async code will run fully synchronously to the end.
Since getDataAsync is executed on the same thread as the MyViewModel constructor, you can run into a deadlock, since the thread waits for itself. When you use ConfigureAwait, you can avoid this situation.
Whether you actually should do that is a different question. What you are actually doing with ConfigureAwait is to start the task and to allow the await to continue on a separate context (which can be on a separate thread). Apart from the fact that you are not even using await here, continuation in a different context can become a problem when you want to execute UI operations after the await.
If you want to be sure that you can wait for getDataAsync in your constructor, you can use Task.Run to force execution in the thread pool:
var getDataTask = Task.Run((Func<Task>)getDataAsync);
//Do something else
getDataTask.Wait();
Everything you execute between Task.Run and the Wait() can run during the execution of getDataAsync. Whether parallelism is actually worth it here depends on what else you do until the Wait().
What you are doing in the MyViewModel constructor is to finally synchronize all asynchronous operations and make the execution of the constructor synchronous. If you want to run that operation asynchronously, you would need to start another task to do it. So you should be really sure that beyond that point async is not required anymore. If it is, run the initialization on another async method, await getDataAsync() there and synchronize somewhere up the call chain.

Are non-thread-safe functions async safe?

Consider the following async function that modifies a non-thread-safe list:
async Task AddNewToList(List<Item> list)
{
// Suppose load takes a few seconds
Item item = await LoadNextItem();
list.Add(item);
}
Simply put: Is this safe?
My concern is that one may invoke the async method, and then while it's loading (either on another thread, or as an I/O operation), the caller may modify the list.
Suppose that the caller is partway through the execution of list.Clear(), for example, and suddenly the Load method finishes! What will happen?
Will the task immediately interrupt and run the list.Add(item); code? Or will it wait until the main thread is done with all scheduled CPU tasks (ie: wait for Clear() to finish), before running the code?
Edit: Since I've basically answered this for myself below, here's a bonus question: Why? Why does it immediately interrupt instead of waiting for CPU bound operations to complete? It seems counter-intuitive to not queue itself up, which would be completely safe.
Edit: Here's a different example I tested myself. The comments indicate the order of execution. I am disappointed!
TaskCompletionSource<bool> source;
private async void buttonPrime_click(object sender, EventArgs e)
{
source = new TaskCompletionSource<bool>(); // 1
await source.Task; // 2
source = null; // 4
}
private void buttonEnd_click(object sender, EventArgs e)
{
source.SetResult(true); // 3
MessageBox.Show(source.ToString()); // 5 and exception is thrown
}
No, its not safe. However also consider that the caller might also have spawned a thread and passed the List to its child thread before calling your code, even in a non async environment, which will have the same detrimental effect.
So; although not safe, there is nothing inherently thread-safe about receiving a List from a caller anyway - there is no way of knowing whether the list is actually being processed from other threads that your own.
Short answer
You always need to be careful using async.
Longer answer
It depends on your SynchronizationContext and TaskScheduler, and what you mean by "safe."
When your code awaits something, it creates a continuation and wraps it in a task, which is then posted to the current SynchronizationContext's TaskScheduler. The context will then determine when and where the continuation will run. The default scheduler simply uses the thread pool, but different types of applications can extend the scheduler and provide more sophisticated synchronization logic.
If you are writing an application that has no SynchronizationContext (for example, a console application, or anything in .NET core), the continuation is simply put on the thread pool, and could execute in parallel with your main thread. In this case you must use lock or synchronized objects such as ConcurrentDictionary<> instead of Dictionary<>, for anything other than local references or references that are closed with the task.
If you are writing a WinForms application, the continuations are put in the message queue, and will all execute on the main thread. This makes it safe to use non-synchronized objects. However, there are other worries, such as deadlocks. And of course if you spawn any threads, you must make sure they use lock or Concurrent objects, and any UI invocations must be marshaled back to the UI thread. Also, if you are nutty enough to write a WinForms application with more than one message pump (this is highly unusual) you'd need to worry about synchronizing any common variables.
If you are writing an ASP.NET application, the SynchronizationContext will ensure that, for a given request, no two threads are executing at the same time. Your continuation might run on a different thread (due to a performance feature known as thread agility), but they will always have the same SynchronizationContext and you are guaranteed that no two threads will access your variables at the same time (assuming, of course, they are not static, in which case they span across HTTP requests and must be synchronized). In addition, the pipeline will block parallel requests for the same session so that they execute in series, so your session state is also protected from threading concerns. However you still need to worry about deadlocks.
And of course you can write your own SynchronizationContext and assign it to your threads, meaning that you specify your own synchronization rules that will be used with async.
See also How do yield and await implement flow of control in .NET?
Assuming the "invalid acces" occures in LoadNextItem(): The Task will throw an exception. Since the context is captured it will pass on to the callers thread so list.Add will not be reached.
So, no it's not thread-safe.
Yes I think that could be a problem.
I would return item and add to the list on the main tread.
private async void GetIntButton(object sender, RoutedEventArgs e)
{
List<int> Ints = new List<int>();
Ints.Add(await GetInt());
}
private async Task<int> GetInt()
{
await Task.Delay(100);
return 1;
}
But you have to call from and async so I do not this this would work either.

In which case does TaskCompletionSource.SetResult() run the continuation synchronously?

Initially I thought that all continuations are executed on the threadpool (given a default synchronization context). This however doesn't seem to be the case when I use a TaskCompletionSource.
My code looks something like this:
Task<int> Foo() {
_tcs = new TaskCompletionSource<int>();
return _tcs.Task;
}
async void Bar() {
Console.WriteLine(Thread.Current.ManagedThreadId);
Console.WriteLine($"{Thread.Current.ManagedThreadId} - {await Foo()}");
}
Bar gets called on a specific thread and the TaskCompletionSource stays unset for some time, meaning the returned tasks IsComplete = false. Then after some time, the same thread would proceed to call _tcs.SetResult(x), which by my understanding should run the continuation on the threadpool.
But what I observed in my application is that the thread running the continuation is in fact still the same thread, as if the continuation was invoked synchronously right as SetResult is called.
I even tried setting a breakpoint on the SetResult and stepping over it (and having a breakpoint in the continuation), which in turn actually goes on to call the continuation synchronously.
When exactly does SetResult() immediately call the continuation synchronously?
Initially I thought that all continuations are executed on the threadpool (given a default synchronization context). This however doesn't seem to be the case when I use a TaskCompletionSource.
Actually, when using await, most continuations are executed synchronously.
Marc's answer is great; I just wanted to go into a bit more detail...
TaskCompletionSource<T> by default will operate synchronously when Set* is called. Set* will complete the task and issue the continuations in a single method call. (This means that calling Set* while holding a lock is a recipe for deadlocks.)
I use the weird phrase "issue the continuations" there because it may or may not actually execute them; more on that later.
The TaskCreationOptions.RunContinuationsAsynchronously flag will tell TaskCompletionSource<T> to issue the continuations asynchronously. This breaks apart the completing of the task (which is still done immediately by Set*) from the issuing of the continuations (which is only triggered by the Set* call). So with RunContinuationsAsynchronously, a Set* call will only complete the task; it will not execute the continuations synchronously. (This means that calling Set* while holding a lock is safe.)
But back to the default case, which issues the continuations synchronously.
Each continuation also has a flag; by default a continuation is executed asynchronously, but it can be made synchronous by TaskContinuationOptions.ExecuteSynchronously. (Note that await does use this flag - link is to my blog; technically this is an implementation detail and not officially documented).
However, even if ExecuteSynchronously is specified, there are a number of situations where the continuation is not executed synchronously:
If there is a TaskScheduler associated with the continuation, that scheduler is given the option of rejecting the current thread, in which case the task is queued to that TaskScheduler instead of executing synchronously.
If the current thread is being aborted, then the task is queued elsewhere.
If the current thread's stack is too deep, then the task is queued elsewhere. (This is only a heuristic, and not guaranteed to avoid StackOverflowException).
That's quite a few conditions, but with your simple Console app test, they're all met:
TaskCompletionSource<T> does not specify RunContinuationsAsynchronously.
The continuation (await) does specify ExecuteSynchronously.
The continuation does not have a TaskScheduler specified.
The target thread is able to execute the continuation (not being aborted; stack is ok).
As a general rule, I would say any usage of TaskCompletionSource<T> should specify TaskCreationOptions.RunContinuationsAsynchronously. Personally, I think the semantics are more appropriate and less surprising with that flag.
SetResult usually runs continuations from TCS synchronously. There main exception to this is if you explicitly pass in the TaskContinuationOptions.RunContinuationsAsynchronously flag when creating the TCS (new in .NET 4.6). The other scenario when it runs things asynchronously is if it thinks the current thread is doomed.
This is quite important, because if you're not careful you can end up having calling code take control of a thread that was meant to be doing other work (like: dealing with socket IO).

Does await completely block the thread?

When we use await in the code, generally the awaiters capture the context and use that as callback when the awaited task completed successfully. But, since await is generally a No-Op (No Operation), does it make the underlying thread not being reusable until execution of the awaited task completes?
For Example:
public async Task MethodAAsync()
{
// Execute some logic
var test = await MethodB();
// Execute remaining logic
}
Here, since I need the result back to proceed further, I need to await on that. But, since we are awaiting, does it block the underlying thread resulting thread not being used to execute any tasks?
But, since await is generally a No.Op(No Operation), does it make the
underlying thread not being reusable until execution of awaited task
completes?
I'm not sure where you got this information from, but await is certainly not a No-Op. Calling await on a Task, for example, will invoke a logical call to Task.GetAwaiter, where the TaskAwaiter has to implement INotifyCompletion or ICriticalNotifyCompletion interface, which tells the compiler how to invoke the continuation (everything after the first await).
The complete structure of async-await transforms your call to a state-machine such that when the state-machine hits the first await, it will first check to see if the called method completed, and if not will register the continuation and return from that method call. Later, once that method completes, it will re-enter the state-machine in order to complete the method. And that is logically how you see the line after await being hit.
But, since we are awaiting, does it block the underlying thread
resulting thread not being used to execute any tasks?
No, creating an entire mechanism only to block the calling thread would be useless. async-await allow you to actually yield the calling thread back to the caller which allows him to continue execution on that same thread, while the runtime takes care of queuing and invoking the completion.
In short, no, it doesn't block your thread.
More info: http://blog.stephencleary.com/2013/11/there-is-no-thread.html
The thread has a local queue of Tasks. Once it hits an await it will pick up another Task from it's queue (assuming all methods up it's stack are awaited). If the queue is empty, it will try to get a Task from the global queue, and if it's empty too - the thread will try to get a Task from another thread's own local queue. In a simple program, where all threads might get "out of Tasks" - the thread will remain idle until one of the Tasks returns.

Async/Await - is it *concurrent*?

I've been considering the new async stuff in C# 5, and one particular question came up.
I understand that the await keyword is a neat compiler trick/syntactic sugar to implement continuation passing, where the remainder of the method is broken up into Task objects and queued-up to be run in order, but where control is returned to the calling method.
My problem is that I've heard that currently this is all on a single thread. Does this mean that this async stuff is really just a way of turning continuation code into Task objects and then calling Application.DoEvents() after each task completes before starting the next one?
Or am I missing something? (This part of the question is rhetorical - I'm fully aware I'm missing something :) )
It is concurrent, in the sense that many outstanding asychronous operations may be in progress at any time. It may or may not be multithreaded.
By default, await will schedule the continuation back to the "current execution context". The "current execution context" is defined as SynchronizationContext.Current if it is non-null, or TaskScheduler.Current if there's no SynchronizationContext.
You can override this default behavior by calling ConfigureAwait and passing false for the continueOnCapturedContext parameter. In that case, the continuation will not be scheduled back to that execution context. This usually means it will be run on a threadpool thread.
Unless you're writing library code, the default behavior is exactly what's desired. WinForms, WPF, and Silverlight (i.e., all the UI frameworks) supply a SynchronizationContext, so the continuation executes on the UI thread (and can safely access UI objects). ASP.NET also supplies a SynchronizationContext that ensures the continuation executes in the correct request context.
Other threads (including threadpool threads, Thread, and BackgroundWorker) do not supply a SynchronizationContext. So Console apps and Win32 services by default do not have a SynchronizationContext at all. In this situation, continuations execute on threadpool threads. This is why Console app demos using await/async include a call to Console.ReadLine/ReadKey or do a blocking Wait on a Task.
If you find yourself needing a SynchronizationContext, you can use AsyncContext from my Nito.AsyncEx library; it basically just provides an async-compatible "main loop" with a SynchronizationContext. I find it useful for Console apps and unit tests (VS2012 now has built-in support for async Task unit tests).
For more information about SynchronizationContext, see my Feb MSDN article.
At no time is DoEvents or an equivalent called; rather, control flow returns all the way out, and the continuation (the rest of the function) is scheduled to be run later. This is a much cleaner solution because it doesn't cause reentrancy issues like you would have if DoEvents was used.
The whole idea behind async/await is that it performs continuation passing nicely, and doesn't allocate a new thread for the operation. The continuation may occur on a new thread, it may continue on the same thread.
The real "meat" (the asynchronous) part of async/await is normally done separately and the communication to the caller is done through TaskCompletionSource. As written here http://blogs.msdn.com/b/pfxteam/archive/2009/06/02/9685804.aspx
The TaskCompletionSource type serves two related purposes, both alluded to by its name: it is a source for creating a task, and the source for that task’s completion. In essence, a TaskCompletionSource acts as the producer for a Task and its completion.
and the example is quite clear:
public static Task<T> RunAsync<T>(Func<T> function)
{
if (function == null) throw new ArgumentNullException(“function”);
var tcs = new TaskCompletionSource<T>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
T result = function();
tcs.SetResult(result);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
Through the TaskCompletionSource you have access to a Task object that you can await, but it isn't through the async/await keywords that you created the multithreading.
Note that when many "slow" functions will be converted to the async/await syntax, you won't need to use TaskCompletionSource very much. They'll use it internally (but in the end somewhere there must be a TaskCompletionSource to have an asynchronous result)
The way I like to explain it is that the "await" keyword simply waits for a task to finish but yields execution to the calling thread while it waits. It then returns the result of the Task and continues from the statement after the "await" keyword once the Task is complete.
Some people I have noticed seem to think that the Task is run in the same thread as the calling thread, this is incorrect and can be proved by trying to alter a Windows.Forms GUI element within the method that await calls. However, the continuation is run in the calling thread where ever possible.
Its just a neat way of not having to have callback delegates or event handlers for when the Task completes.
I feel like this question needs a simpler answer for people. So I'm going to oversimplify.
The fact is, if you save the Tasks and don't await them, then async/await is "concurrent".
var a = await LongTask1(x);
var b = await LongTask2(y);
var c = ShortTask(a, b);
is not concurrent. LongTask1 will complete before LongTask2 starts.
var a = LongTask1(x);
var b = LongTask2(y);
var c = ShortTask(await a, await b);
is concurrent.
While I also urge people to get a deeper understanding and read up on this, you can use async/await for concurrency, and it's pretty simple.

Categories

Resources