I am working on a multi threaded WindowsPhone8 app that has critical sections within async methods.
Does anyone know of a way to properly use semaphores / mutexes in C# where you are using nested async calls where the inner method may be acquiring the same lock that it already acquired up the callstack? I thought the SemaphoreSlim might be the answer, but it looks like it causes a deadlock.
public class Foo
{
SemaphoreSlim _lock = new SemaphoreSlim(1);
public async Task Bar()
{
await _lock.WaitAsync();
await BarInternal();
_lock.Release();
}
public async Task BarInternal()
{
await _lock.WaitAsync(); // deadlock
// DO work
_lock.Release();
}
}
Recursive locks are a really bad idea (IMO; link is to my own blog). This is especially true for async code. It's wicked difficult to get async-compatible recursive locks working. I have a proof-of-concept here but fair warning: I do not recommend using this code in production, this code will not be rolled into AsyncEx, and it is not thoroughly tested.
What you should do instead is restructure your code as #svick stated. Something like this:
public async Task Bar()
{
await _lock.WaitAsync();
await BarInternal_UnderLock();
_lock.Release();
}
public async Task BarInternal()
{
await _lock.WaitAsync();
await BarInternal_UnderLock();
_lock.Release();
}
private async Task BarInternal_UnderLock()
{
// DO work
}
Here's what I did in such a situation (still, I'm not experienced with tasks, so don't beat me ;-)
So basically you have move the actual implementation to non locking methods and use these in all methods which acquire a lock.
public class Foo
{
SemaphoreSlim _lock = new SemaphoreSlim(1);
public async Task Bar()
{
await _lock.WaitAsync();
await BarNoLock();
_lock.Release();
}
public async Task BarInternal()
{
await _lock.WaitAsync(); // no deadlock
await BarNoLock();
_lock.Release();
}
private async Task BarNoLock()
{
// do the work
}
}
First, read through Stephen Cleary's blog post, which he linked to in his answer. He mentions multiple reasons, such as uncertain lock state and inconsistent invariants, which are associated with recursive locks (not to mention recursive async locks). If you can do the refactoring he and Knickedi describe in their answers, that would be great.
However, there are some cases where that type of refactoring is just not possible. Fortunately, there are now multiple libraries which support nested async calls (lock reentrance). Here are two. The author of the first has a blog post where he talks more about it.
https://github.com/neosmart/AsyncLock
https://github.com/mysteryjeans/Flettu/
You can incorporate it into your code as such (using the first library in this example):
public class Foo
{
AsyncLock _lock = new AsyncLock();
public async Task Bar()
{
// This first LockAsync() call should not block
using (await _lock.LockAsync())
{
await BarInternal();
}
}
public async Task BarInternal()
{
// This second call to LockAsync() will be recognized
// as being a reëntrant call and go through
using (await _lock.LockAsync()) // no deadlock
{
// do work
}
}
}
Disclaimer: I'm the author of the NuGet package I mention here.
There have been several attempts at a recursive/reentrant async lock (some are listed below) but only one of them succeeds in providing all three of these at once:
Asynchronicity
Reentrance
Mutual exclusion
As of this writing, the only correct implementation that I know of is:
https://www.nuget.org/packages/ReentrantAsyncLock/
The package documentation shows how to use it. Using it in your code would look like this:
public class Foo
{
ReentrantAsyncLock _lock = new ReentrantAsyncLock();
public async Task Bar()
{
await using (await _lock.LockAsync(CancellationToken.None))
{
await BarInternal();
}
}
public async Task BarInternal()
{
await using (await _lock.LockAsync(CancellationToken.None)) // No deadlock
{
// DO work
}
}
}
I'm sure everyone knows what asynchronicity is.
Your code is an example of reentrance.
This is an example of mutual exclusion:
var gate = new object();
var value = 0;
var tasks = new List<Task>();
for (var i = 0; i < 1000; i++)
{
var task = Task.Run(() =>
{
lock (gate)
{
value++; // Without the lock this is a race condition
}
});
tasks.Add(task);
}
Task.WhenAll(tasks).Wait();
Debug.Assert(value == 1000);
The regular lock keyword in C# gives reentrance and mutual exclusion.
SemaphoreSlim and a dozen other things give asynchronicity and mutual exclusion.
But it has been difficult for people to get all three together at once.
For example, Stephen Cleary linked to his proof of concept in his answer. But his fails these tests:
https://github.com/matthew-a-thomas/cs-reentrant-async-lock/blob/ece6e461c26f005da2122185cb9c5b884968f98a/ReentrantAsyncLock.Tests/ReentrantAsyncLockClass.cs
(Keep in mind those tests were originally written for the ReentrantAsyncLock NuGet package, that's why some things are commented out that don't make sense for Stephen Cleary's RecursiveAsyncLock, and that's why the test file has ReentrantAsyncLock in its name. Compare that test file to the equivalent on the main branch and you'll see what I mean.)
Of course he has never claimed that it would but has only ever cautioned people against using it. So this isn't a ding against Stephen. I'm just giving an example of how someone can make an async lock and at first glance it looks like it gives all three of the things I listed above when in fact it has trouble putting the second two things together.
Similar things can be said for all of these:
https://github.com/neosmart/AsyncLock - these two tests deadlock
https://github.com/mysteryjeans/Flettu/ - this test sometimes deadlocks, sometimes fails with a mismatched count, and sometimes fails with a semaphore count exception
https://github.com/jasonkuo41/CellWars.Threading.AsyncLock - both of these tests fail with mismatched values
You can use the System.Threading.ReaderWriterLockSlim (doc), which has a support recursion flag:
ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
async Task Bar()
{
try
{
_lock.EnterReadLock();
await BarInternal();
}
finally
{
if (_lock.IsReadLockHeld)
_lock.ExitReadLock();
}
}
async Task BarInternal()
{
try
{
_lock.EnterReadLock();
await Task.Delay(1000);
}
finally
{
if (_lock.IsReadLockHeld)
_lock.ExitReadLock();
}
}
Still you should be very careful with recursion because it is very difficult to control which thread took a lock and when.
The code in the question will be result in a deadlock because it tries to acquire the lock twice, something like:
await _lock.WaitAsync();
await _lock.WaitAsync(); --> Will result in exception.
While flagging the ReaderWriterLockSlim in SupportsRecursion will not throw an exception for this weird code:
_lock.EnterReadLock();
_lock.EnterReadLock();
Related
I am trying to lock a section of my code that is synchronous and release the lock in an asynchronous code block (after an execution of a task). I have read about 'AutoResetEvent', and wanted to implement its signaling functionality between the caller and the callee of the code implementation.
The aim is to lock the code that is responsible for verifying whether the transaction was administered before. After the check is done and the transaction was saved, I release the code. The check and saving operations are asynchronous, and the lock release happens at 'continueWith' delegate function.
I have the following abstraction and implementation of AutoResetEvent:
public interface IThreadLockKeyProvider { public AutoResetEvent PayLock { get; } }
public class ThreadLockKeyProvider : IThreadLockKeyProvider
{
public static readonly AutoResetEvent _payLock;
static ThreadLockKeyProvider()
{
_payLock = new AutoResetEvent(true);
}
public AutoResetEvent PayLock { get { return _payLock; } }
}
The instance is injected as a singleton in .net core dependency injection container.
The code of the eventhandler method is the following:
public Task<Unit> Handle(ProcessPaymentCommand command, CancellationToken cancellationToken)
{
_threadLockKeyProvider.PayLock.WaitOne();
var res = _brandDepositStrategy.AdministerDeposit().Result;
return Task.FromResult(Unit.Value);
}
The AdministerDeposit method that releases the lock.
public override async Task<Task> AdministerDeposit()
{
Task<PaymentTransactions> dbOperation = _transactionAdministrationFacade.UpdateDbTransactionAsync(_IPNRequestDto);
return await dbOperation.ContinueWith(async x =>
{
_threadLockKeyProvider.PayLock.Set();
if (x.IsFaulted)
throw x.Exception;
_transactionAdministrationFacade.CallBackDto = await _responseComposer.GetPaymentResponseDto(x.Result, _IPNRequestDto);
await CreateDepositSF();
});
}
The question is whether the implementation cause any problems?
Note: The code does work. It doesnt crash and pass the unit tests that I built.
in theory there's nothing wrong with AutoResetEvent in this scenario, but: it isn't exactly built for async, which means that your WaitOne() blocks a thread - exactly what you're trying to avoid. Likewise, you're using sync-over-async. Since this is basically an async lock, seeing the entry/exit so far apart concerns me - it makes it very easy to introduce errors.
Let's try and fix those things:
use SemaphoreSlim as an async-friendly lock
await our result
move the entry/exit of the lock into the same place
example:
private readonly SemaphoreSlim _lock = new(1, 1);
public async Task<int> SomeOuterMethodAsync(CancellationToken cancellationToken)
{
await _lock.WaitAsync(cancellationToken);
try
{
var s = await SomeInnerMethodAsync(cancellationToken);
return s.Length; // just some projection
}
finally
{
_lock.Release();
}
}
// note: this could be in unrelated types/instances/etc
private Task<string> SomeInnerMethodAsync(CancellationToken cancellationToken)
=> throw new InvalidOperationException("not shown");
I have an Asp.Net Core 6 Web Api.
I have a Singleton class with several methods in it.
I want only 1 thread to enter any of the methods of the class at a time.
Is it ok to initialize the SemaphoreSlim class in the constructor and use it in every method in the following way? Are there any dangers from it?
Is there a better way to achieve what I am looking for?
public class Foo
{
private readonly SemaphoreSlim _sm;
public Foo()
{
_sm = new SemaphoreSlim(1, 1);
}
public async Task FirstMethod()
{
await _sm.WaitAsync();
//Do some work
_sm.Release();
}
public async Task SecondMethod()
{
await _sm.WaitAsync();
//Do some work
_sm.Release();
}
}
You should adopt the try/catch/finalize pattern - if you do so, you're mostly safe: all code under the WaitAsync will be executed as defined by the max semaphore. Only if the code under the semaphore block, you'll get a deadlock - you could consider using a timeout, but typically this is accepted.
public async Task SecondMethod()
{
await _sm.WaitAsync();
try
{
//Do some work
}
finally
{
//release in case of errors
_sm.Release();
}
}
Other stuff to consider is, especially if it is a long running process, is to use a cancellation token to indicate application closure.
Also keep in mind that if you fire a lot of these methods from various threads, the order is not guaranteed - but they will all be handled.
I have a public async void Foo() method that I want to call from synchronous method. So far all I have seen from MSDN documentation is calling async methods via async methods, but my whole program is not built with async methods.
Is this even possible?
Here's one example of calling these methods from an asynchronous method:
Walkthrough: Accessing the Web by Using Async and Await (C# and Visual Basic)
Now I'm looking into calling these async methods from sync methods.
Asynchronous programming does "grow" through the code base. It has been compared to a zombie virus. The best solution is to allow it to grow, but sometimes that's not possible.
I have written a few types in my Nito.AsyncEx library for dealing with a partially-asynchronous code base. There's no solution that works in every situation, though.
Solution A
If you have a simple asynchronous method that doesn't need to synchronize back to its context, then you can use Task.WaitAndUnwrapException:
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
You do not want to use Task.Wait or Task.Result because they wrap exceptions in AggregateException.
This solution is only appropriate if MyAsyncMethod does not synchronize back to its context. In other words, every await in MyAsyncMethod should end with ConfigureAwait(false). This means it can't update any UI elements or access the ASP.NET request context.
Solution B
If MyAsyncMethod does need to synchronize back to its context, then you may be able to use AsyncContext.RunTask to provide a nested context:
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
*Update 4/14/2014: In more recent versions of the library the API is as follows:
var result = AsyncContext.Run(MyAsyncMethod);
(It's OK to use Task.Result in this example because RunTask will propagate Task exceptions).
The reason you may need AsyncContext.RunTask instead of Task.WaitAndUnwrapException is because of a rather subtle deadlock possibility that happens on WinForms/WPF/SL/ASP.NET:
A synchronous method calls an async method, obtaining a Task.
The synchronous method does a blocking wait on the Task.
The async method uses await without ConfigureAwait.
The Task cannot complete in this situation because it only completes when the async method is finished; the async method cannot complete because it is attempting to schedule its continuation to the SynchronizationContext, and WinForms/WPF/SL/ASP.NET will not allow the continuation to run because the synchronous method is already running in that context.
This is one reason why it's a good idea to use ConfigureAwait(false) within every async method as much as possible.
Solution C
AsyncContext.RunTask won't work in every scenario. For example, if the async method awaits something that requires a UI event to complete, then you'll deadlock even with the nested context. In that case, you could start the async method on the thread pool:
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
However, this solution requires a MyAsyncMethod that will work in the thread pool context. So it can't update UI elements or access the ASP.NET request context. And in that case, you may as well add ConfigureAwait(false) to its await statements, and use solution A.
Update, 2019-05-01: The current "least-worst practices" are in an MSDN article here.
Adding a solution that finally solved my problem, hopefully saves somebody's time.
Firstly read a couple articles of Stephen Cleary:
Async and Await
Don't Block on Async Code
From the "two best practices" in "Don't Block on Async Code", the first one didn't work for me and the second one wasn't applicable (basically if I can use await, I do!).
So here is my workaround: wrap the call inside a Task.Run<>(async () => await FunctionAsync()); and hopefully no deadlock anymore.
Here is my code:
public class LogReader
{
ILogger _logger;
public LogReader(ILogger logger)
{
_logger = logger;
}
public LogEntity GetLog()
{
Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync());
return task.Result;
}
public async Task<LogEntity> GetLogAsync()
{
var result = await _logger.GetAsync();
// more code here...
return result as LogEntity;
}
}
Microsoft built an AsyncHelper (internal) class to run Async as Sync. The source looks like:
internal static class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return AsyncHelper._myTaskFactory
.StartNew<Task<TResult>>(func)
.Unwrap<TResult>()
.GetAwaiter()
.GetResult();
}
public static void RunSync(Func<Task> func)
{
AsyncHelper._myTaskFactory
.StartNew<Task>(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
}
The Microsoft.AspNet.Identity base classes only have Async methods and in order to call them as Sync there are classes with extension methods that look like (example usage):
public static TUser FindById<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
return AsyncHelper.RunSync<TUser>(() => manager.FindByIdAsync(userId));
}
public static bool IsInRole<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId, string role) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
return AsyncHelper.RunSync<bool>(() => manager.IsInRoleAsync(userId, role));
}
For those concerned about the licensing terms of code, here is a link to very similar code (just adds support for culture on the thread) that has comments to indicate that it is MIT Licensed by Microsoft. https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs
Wouldn't this be the same as just calling Task.Run(async ()=> await AsyncFunc()).Result? AFAIK, Microsoft is now discouraging from calling TaskFactory.StartNew, since they are both equivalent and one is more readable than the other.
Absolutely not.
The easy answer is that
.Unwrap().GetAwaiter().GetResult() != .Result
First off the
Is Task.Result the same as .GetAwaiter.GetResult()?
Secondly .Unwrap() causes the setup of the Task not to block the wrapped task.
Which should lead anyone to ask
Wouldn't this be the same as just calling Task.Run(async ()=> await AsyncFunc()).GetAwaiter().GetResult()
Which would then be a It Depends.
Regarding usage of Task.Start() , Task.Run() and Task.Factory.StartNew()
Excerpt:
Task.Run uses TaskCreationOptions.DenyChildAttach which means that children's tasks can not be attached to the parent and it uses TaskScheduler.Default which means that the one that runs tasks on Thread Pool will always be used to run tasks.
Task.Factory.StartNew uses TaskScheduler.Current which means scheduler of the current thread, it might be TaskScheduler.Default but not always.
Additional Reading:
Specifying a synchronization context
ASP.NET Core SynchronizationContext
For extra safety, wouldn't it be better to call it like this AsyncHelper.RunSync(async () => await AsyncMethod().ConfigureAwait(false)); This way we're telling the "inner" method "please don't try to sync to upper context and dealock"
Really great point and as most object architectural questions go it depends.
As an extension method do you want to force that for absolutely every call, or do you let the programmer using the function configure that on their own async calls? I could see a use case for call three scenarios; it most likely is not something you want in WPF, certainly makes sense in most cases, but considering there is no Context in ASP.Net Core if you could guarantee it was say internal for a ASP.Net Core, then it wouldn't matter.
async Main is now part of C# 7.2 and can be enabled in the projects advanced build settings.
For C# < 7.2, the correct way is:
static void Main(string[] args)
{
MainAsync().GetAwaiter().GetResult();
}
static async Task MainAsync()
{
/*await stuff here*/
}
You'll see this used in a lot of Microsoft documentation, for example:
https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use-topics-subscriptions
I'm not 100% sure, but I believe the technique described in this blog should work in many circumstances:
You can thus use task.GetAwaiter().GetResult() if you want to directly invoke this propagation logic.
public async Task<string> StartMyTask()
{
await Foo()
// code to execute once foo is done
}
static void Main()
{
var myTask = StartMyTask(); // call your method which will return control once it hits await
// now you can continue executing code here
string result = myTask.Result; // wait for the task to complete to continue
// use result
}
You read the 'await' keyword as "start this long running task, then return control to the calling method". Once the long-running task is done, then it executes the code after it. The code after the await is similar to what used to be CallBack methods. The big difference being the logical flow is not interrupted which makes it much easier to write and read.
There is, however, a good solution that works in (almost: see comments) every situation: an ad-hoc message pump (SynchronizationContext).
The calling thread will be blocked as expected, while still ensuring that all continuations called from the async function don't deadlock as they'll be marshaled to the ad-hoc SynchronizationContext (message pump) running on the calling thread.
The code of the ad-hoc message pump helper:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Threading
{
/// <summary>Provides a pump that supports running asynchronous methods on the current thread.</summary>
public static class AsyncPump
{
/// <summary>Runs the specified asynchronous method.</summary>
/// <param name="asyncMethod">The asynchronous method to execute.</param>
public static void Run(Action asyncMethod)
{
if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");
var prevCtx = SynchronizationContext.Current;
try
{
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext(true);
SynchronizationContext.SetSynchronizationContext(syncCtx);
// Invoke the function
syncCtx.OperationStarted();
asyncMethod();
syncCtx.OperationCompleted();
// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
}
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}
/// <summary>Runs the specified asynchronous method.</summary>
/// <param name="asyncMethod">The asynchronous method to execute.</param>
public static void Run(Func<Task> asyncMethod)
{
if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");
var prevCtx = SynchronizationContext.Current;
try
{
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext(false);
SynchronizationContext.SetSynchronizationContext(syncCtx);
// Invoke the function and alert the context to when it completes
var t = asyncMethod();
if (t == null) throw new InvalidOperationException("No task provided.");
t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);
// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
t.GetAwaiter().GetResult();
}
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}
/// <summary>Runs the specified asynchronous method.</summary>
/// <param name="asyncMethod">The asynchronous method to execute.</param>
public static T Run<T>(Func<Task<T>> asyncMethod)
{
if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");
var prevCtx = SynchronizationContext.Current;
try
{
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext(false);
SynchronizationContext.SetSynchronizationContext(syncCtx);
// Invoke the function and alert the context to when it completes
var t = asyncMethod();
if (t == null) throw new InvalidOperationException("No task provided.");
t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);
// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
return t.GetAwaiter().GetResult();
}
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}
/// <summary>Provides a SynchronizationContext that's single-threaded.</summary>
private sealed class SingleThreadSynchronizationContext : SynchronizationContext
{
/// <summary>The queue of work items.</summary>
private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =
new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
/// <summary>The processing thread.</summary>
private readonly Thread m_thread = Thread.CurrentThread;
/// <summary>The number of outstanding operations.</summary>
private int m_operationCount = 0;
/// <summary>Whether to track operations m_operationCount.</summary>
private readonly bool m_trackOperations;
/// <summary>Initializes the context.</summary>
/// <param name="trackOperations">Whether to track operation count.</param>
internal SingleThreadSynchronizationContext(bool trackOperations)
{
m_trackOperations = trackOperations;
}
/// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
/// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
/// <param name="state">The object passed to the delegate.</param>
public override void Post(SendOrPostCallback d, object state)
{
if (d == null) throw new ArgumentNullException("d");
m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
}
/// <summary>Not supported.</summary>
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException("Synchronously sending is not supported.");
}
/// <summary>Runs an loop to process all queued work items.</summary>
public void RunOnCurrentThread()
{
foreach (var workItem in m_queue.GetConsumingEnumerable())
workItem.Key(workItem.Value);
}
/// <summary>Notifies the context that no more work will arrive.</summary>
public void Complete() { m_queue.CompleteAdding(); }
/// <summary>Invoked when an async operation is started.</summary>
public override void OperationStarted()
{
if (m_trackOperations)
Interlocked.Increment(ref m_operationCount);
}
/// <summary>Invoked when an async operation is completed.</summary>
public override void OperationCompleted()
{
if (m_trackOperations &&
Interlocked.Decrement(ref m_operationCount) == 0)
Complete();
}
}
}
}
Usage:
AsyncPump.Run(() => FooAsync(...));
More detailed description of the async pump is available here.
To anyone paying attention to this question anymore...
If you look in Microsoft.VisualStudio.Services.WebApi there's a class called TaskExtensions. Within that class you'll see the static extension method Task.SyncResult(), which like totally just blocks the thread till the task returns.
Internally it calls task.GetAwaiter().GetResult() which is pretty simple, however it's overloaded to work on any async method that return Task, Task<T> or Task<HttpResponseMessage>... syntactic sugar, baby... daddy's got a sweet tooth.
It looks like ...GetAwaiter().GetResult() is the MS-official way to execute async code in a blocking context. Seems to work very fine for my use case.
var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);
OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();
Or use this:
var result=result.GetAwaiter().GetResult().AccessToken
You can call any asynchronous method from synchronous code, that is, until you need to await on them, in which case they have to be marked as async too.
As a lot of people are suggesting here, you could call Wait() or Result on the resulting task in your synchronous method, but then you end up with a blocking call in that method, which sort of defeats the purpose of async.
If you really can't make your method async and you don't want to lock up the synchronous method, then you're going to have to use a callback method by passing it as parameter to the ContinueWith() method on task.
Inspired by some of the other answers, I created the following simple helper methods:
public static TResult RunSync<TResult>(Func<Task<TResult>> method)
{
var task = method();
return task.GetAwaiter().GetResult();
}
public static void RunSync(Func<Task> method)
{
var task = method();
task.GetAwaiter().GetResult();
}
They can be called as follows (depending on whether you are returning a value or not):
RunSync(() => Foo());
var result = RunSync(() => FooWithResult());
Note that the signature in the original question public async void Foo() is incorrect. It should be public async Task Foo() as you should return Task not void for async methods that don't return a value (yes, there are some rare exceptions).
Stephen Cleary's Answer;
That approach shouldn't cause a deadlock (assuming that
ProblemMethodAsync doesn't send updates to the UI thread or anything
like that). It does assume that ProblemMethodAsync can be called on a
thread pool thread, which is not always the case.
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
And here is the approach;
The Thread Pool Hack A similar approach to the Blocking Hack is to
offload the asynchronous work to the thread pool, then block on the
resulting task. The code using this hack would look like the code
shown in Figure 7.
Figure 7 Code for the Thread Pool Hack
C#
public sealed class WebDataService : IDataService
{
public string Get(int id)
{
return Task.Run(() => GetAsync(id)).GetAwaiter().GetResult();
}
public async Task<string> GetAsync(int id)
{
using (var client = new WebClient())
return await client.DownloadStringTaskAsync(
"https://www.example.com/api/values/" + id);
}
}
The call to Task.Run executes the asynchronous method on a thread pool
thread. Here it will run without a context, thus avoiding the
deadlock. One of the problems with this approach is the asynchronous
method can’t depend on executing within a specific context. So, it
can’t use UI elements or the ASP.NET HttpContext.Current.
Here is the simplest solution. I saw it somewhere on the Internet, I didn't remember where, but I have been using it successfully. It will not deadlock the calling thread.
void SynchronousFunction()
{
Task.Run(Foo).Wait();
}
string SynchronousFunctionReturnsString()
{
return Task.Run(Foo).Result;
}
string SynchronousFunctionReturnsStringWithParam(int id)
{
return Task.Run(() => Foo(id)).Result;
}
After hours of trying different methods, with more or less success, this is what I ended with. It doesn't end in a deadlock while getting result and it also gets and throws the original exception and not the wrapped one.
private ReturnType RunSync()
{
var task = Task.Run(async () => await myMethodAsync(agency));
if (task.IsFaulted && task.Exception != null)
{
throw task.Exception;
}
return task.Result;
}
You can now use source generators to create a sync version of your method using Sync Method Generator library (nuget).
Use it as follows:
[Zomp.SyncMethodGenerator.CreateSyncVersion]
public async void FooAsync()
Which will generate Foo method which you can call synchronously.
Those windows async methods have a nifty little method called AsTask(). You can use this to have the method return itself as a task so that you can manually call Wait() on it.
For example, on a Windows Phone 8 Silverlight application, you can do the following:
private void DeleteSynchronous(string path)
{
StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
Task t = localFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask();
t.Wait();
}
private void FunctionThatNeedsToBeSynchronous()
{
// Do some work here
// ....
// Delete something in storage synchronously
DeleteSynchronous("pathGoesHere");
// Do other work here
// .....
}
Hope this helps!
If you want to run it Sync
MethodAsync().RunSynchronously()
I have a public async void Foo() method that I want to call from synchronous method. So far all I have seen from MSDN documentation is calling async methods via async methods, but my whole program is not built with async methods.
Is this even possible?
Here's one example of calling these methods from an asynchronous method:
Walkthrough: Accessing the Web by Using Async and Await (C# and Visual Basic)
Now I'm looking into calling these async methods from sync methods.
Asynchronous programming does "grow" through the code base. It has been compared to a zombie virus. The best solution is to allow it to grow, but sometimes that's not possible.
I have written a few types in my Nito.AsyncEx library for dealing with a partially-asynchronous code base. There's no solution that works in every situation, though.
Solution A
If you have a simple asynchronous method that doesn't need to synchronize back to its context, then you can use Task.WaitAndUnwrapException:
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
You do not want to use Task.Wait or Task.Result because they wrap exceptions in AggregateException.
This solution is only appropriate if MyAsyncMethod does not synchronize back to its context. In other words, every await in MyAsyncMethod should end with ConfigureAwait(false). This means it can't update any UI elements or access the ASP.NET request context.
Solution B
If MyAsyncMethod does need to synchronize back to its context, then you may be able to use AsyncContext.RunTask to provide a nested context:
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
*Update 4/14/2014: In more recent versions of the library the API is as follows:
var result = AsyncContext.Run(MyAsyncMethod);
(It's OK to use Task.Result in this example because RunTask will propagate Task exceptions).
The reason you may need AsyncContext.RunTask instead of Task.WaitAndUnwrapException is because of a rather subtle deadlock possibility that happens on WinForms/WPF/SL/ASP.NET:
A synchronous method calls an async method, obtaining a Task.
The synchronous method does a blocking wait on the Task.
The async method uses await without ConfigureAwait.
The Task cannot complete in this situation because it only completes when the async method is finished; the async method cannot complete because it is attempting to schedule its continuation to the SynchronizationContext, and WinForms/WPF/SL/ASP.NET will not allow the continuation to run because the synchronous method is already running in that context.
This is one reason why it's a good idea to use ConfigureAwait(false) within every async method as much as possible.
Solution C
AsyncContext.RunTask won't work in every scenario. For example, if the async method awaits something that requires a UI event to complete, then you'll deadlock even with the nested context. In that case, you could start the async method on the thread pool:
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
However, this solution requires a MyAsyncMethod that will work in the thread pool context. So it can't update UI elements or access the ASP.NET request context. And in that case, you may as well add ConfigureAwait(false) to its await statements, and use solution A.
Update, 2019-05-01: The current "least-worst practices" are in an MSDN article here.
Adding a solution that finally solved my problem, hopefully saves somebody's time.
Firstly read a couple articles of Stephen Cleary:
Async and Await
Don't Block on Async Code
From the "two best practices" in "Don't Block on Async Code", the first one didn't work for me and the second one wasn't applicable (basically if I can use await, I do!).
So here is my workaround: wrap the call inside a Task.Run<>(async () => await FunctionAsync()); and hopefully no deadlock anymore.
Here is my code:
public class LogReader
{
ILogger _logger;
public LogReader(ILogger logger)
{
_logger = logger;
}
public LogEntity GetLog()
{
Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync());
return task.Result;
}
public async Task<LogEntity> GetLogAsync()
{
var result = await _logger.GetAsync();
// more code here...
return result as LogEntity;
}
}
Microsoft built an AsyncHelper (internal) class to run Async as Sync. The source looks like:
internal static class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return AsyncHelper._myTaskFactory
.StartNew<Task<TResult>>(func)
.Unwrap<TResult>()
.GetAwaiter()
.GetResult();
}
public static void RunSync(Func<Task> func)
{
AsyncHelper._myTaskFactory
.StartNew<Task>(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
}
The Microsoft.AspNet.Identity base classes only have Async methods and in order to call them as Sync there are classes with extension methods that look like (example usage):
public static TUser FindById<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
return AsyncHelper.RunSync<TUser>(() => manager.FindByIdAsync(userId));
}
public static bool IsInRole<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId, string role) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
return AsyncHelper.RunSync<bool>(() => manager.IsInRoleAsync(userId, role));
}
For those concerned about the licensing terms of code, here is a link to very similar code (just adds support for culture on the thread) that has comments to indicate that it is MIT Licensed by Microsoft. https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs
Wouldn't this be the same as just calling Task.Run(async ()=> await AsyncFunc()).Result? AFAIK, Microsoft is now discouraging from calling TaskFactory.StartNew, since they are both equivalent and one is more readable than the other.
Absolutely not.
The easy answer is that
.Unwrap().GetAwaiter().GetResult() != .Result
First off the
Is Task.Result the same as .GetAwaiter.GetResult()?
Secondly .Unwrap() causes the setup of the Task not to block the wrapped task.
Which should lead anyone to ask
Wouldn't this be the same as just calling Task.Run(async ()=> await AsyncFunc()).GetAwaiter().GetResult()
Which would then be a It Depends.
Regarding usage of Task.Start() , Task.Run() and Task.Factory.StartNew()
Excerpt:
Task.Run uses TaskCreationOptions.DenyChildAttach which means that children's tasks can not be attached to the parent and it uses TaskScheduler.Default which means that the one that runs tasks on Thread Pool will always be used to run tasks.
Task.Factory.StartNew uses TaskScheduler.Current which means scheduler of the current thread, it might be TaskScheduler.Default but not always.
Additional Reading:
Specifying a synchronization context
ASP.NET Core SynchronizationContext
For extra safety, wouldn't it be better to call it like this AsyncHelper.RunSync(async () => await AsyncMethod().ConfigureAwait(false)); This way we're telling the "inner" method "please don't try to sync to upper context and dealock"
Really great point and as most object architectural questions go it depends.
As an extension method do you want to force that for absolutely every call, or do you let the programmer using the function configure that on their own async calls? I could see a use case for call three scenarios; it most likely is not something you want in WPF, certainly makes sense in most cases, but considering there is no Context in ASP.Net Core if you could guarantee it was say internal for a ASP.Net Core, then it wouldn't matter.
async Main is now part of C# 7.2 and can be enabled in the projects advanced build settings.
For C# < 7.2, the correct way is:
static void Main(string[] args)
{
MainAsync().GetAwaiter().GetResult();
}
static async Task MainAsync()
{
/*await stuff here*/
}
You'll see this used in a lot of Microsoft documentation, for example:
https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use-topics-subscriptions
I'm not 100% sure, but I believe the technique described in this blog should work in many circumstances:
You can thus use task.GetAwaiter().GetResult() if you want to directly invoke this propagation logic.
public async Task<string> StartMyTask()
{
await Foo()
// code to execute once foo is done
}
static void Main()
{
var myTask = StartMyTask(); // call your method which will return control once it hits await
// now you can continue executing code here
string result = myTask.Result; // wait for the task to complete to continue
// use result
}
You read the 'await' keyword as "start this long running task, then return control to the calling method". Once the long-running task is done, then it executes the code after it. The code after the await is similar to what used to be CallBack methods. The big difference being the logical flow is not interrupted which makes it much easier to write and read.
There is, however, a good solution that works in (almost: see comments) every situation: an ad-hoc message pump (SynchronizationContext).
The calling thread will be blocked as expected, while still ensuring that all continuations called from the async function don't deadlock as they'll be marshaled to the ad-hoc SynchronizationContext (message pump) running on the calling thread.
The code of the ad-hoc message pump helper:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Threading
{
/// <summary>Provides a pump that supports running asynchronous methods on the current thread.</summary>
public static class AsyncPump
{
/// <summary>Runs the specified asynchronous method.</summary>
/// <param name="asyncMethod">The asynchronous method to execute.</param>
public static void Run(Action asyncMethod)
{
if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");
var prevCtx = SynchronizationContext.Current;
try
{
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext(true);
SynchronizationContext.SetSynchronizationContext(syncCtx);
// Invoke the function
syncCtx.OperationStarted();
asyncMethod();
syncCtx.OperationCompleted();
// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
}
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}
/// <summary>Runs the specified asynchronous method.</summary>
/// <param name="asyncMethod">The asynchronous method to execute.</param>
public static void Run(Func<Task> asyncMethod)
{
if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");
var prevCtx = SynchronizationContext.Current;
try
{
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext(false);
SynchronizationContext.SetSynchronizationContext(syncCtx);
// Invoke the function and alert the context to when it completes
var t = asyncMethod();
if (t == null) throw new InvalidOperationException("No task provided.");
t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);
// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
t.GetAwaiter().GetResult();
}
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}
/// <summary>Runs the specified asynchronous method.</summary>
/// <param name="asyncMethod">The asynchronous method to execute.</param>
public static T Run<T>(Func<Task<T>> asyncMethod)
{
if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");
var prevCtx = SynchronizationContext.Current;
try
{
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext(false);
SynchronizationContext.SetSynchronizationContext(syncCtx);
// Invoke the function and alert the context to when it completes
var t = asyncMethod();
if (t == null) throw new InvalidOperationException("No task provided.");
t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);
// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
return t.GetAwaiter().GetResult();
}
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}
/// <summary>Provides a SynchronizationContext that's single-threaded.</summary>
private sealed class SingleThreadSynchronizationContext : SynchronizationContext
{
/// <summary>The queue of work items.</summary>
private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =
new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
/// <summary>The processing thread.</summary>
private readonly Thread m_thread = Thread.CurrentThread;
/// <summary>The number of outstanding operations.</summary>
private int m_operationCount = 0;
/// <summary>Whether to track operations m_operationCount.</summary>
private readonly bool m_trackOperations;
/// <summary>Initializes the context.</summary>
/// <param name="trackOperations">Whether to track operation count.</param>
internal SingleThreadSynchronizationContext(bool trackOperations)
{
m_trackOperations = trackOperations;
}
/// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
/// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
/// <param name="state">The object passed to the delegate.</param>
public override void Post(SendOrPostCallback d, object state)
{
if (d == null) throw new ArgumentNullException("d");
m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
}
/// <summary>Not supported.</summary>
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException("Synchronously sending is not supported.");
}
/// <summary>Runs an loop to process all queued work items.</summary>
public void RunOnCurrentThread()
{
foreach (var workItem in m_queue.GetConsumingEnumerable())
workItem.Key(workItem.Value);
}
/// <summary>Notifies the context that no more work will arrive.</summary>
public void Complete() { m_queue.CompleteAdding(); }
/// <summary>Invoked when an async operation is started.</summary>
public override void OperationStarted()
{
if (m_trackOperations)
Interlocked.Increment(ref m_operationCount);
}
/// <summary>Invoked when an async operation is completed.</summary>
public override void OperationCompleted()
{
if (m_trackOperations &&
Interlocked.Decrement(ref m_operationCount) == 0)
Complete();
}
}
}
}
Usage:
AsyncPump.Run(() => FooAsync(...));
More detailed description of the async pump is available here.
To anyone paying attention to this question anymore...
If you look in Microsoft.VisualStudio.Services.WebApi there's a class called TaskExtensions. Within that class you'll see the static extension method Task.SyncResult(), which like totally just blocks the thread till the task returns.
Internally it calls task.GetAwaiter().GetResult() which is pretty simple, however it's overloaded to work on any async method that return Task, Task<T> or Task<HttpResponseMessage>... syntactic sugar, baby... daddy's got a sweet tooth.
It looks like ...GetAwaiter().GetResult() is the MS-official way to execute async code in a blocking context. Seems to work very fine for my use case.
var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);
OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();
Or use this:
var result=result.GetAwaiter().GetResult().AccessToken
You can call any asynchronous method from synchronous code, that is, until you need to await on them, in which case they have to be marked as async too.
As a lot of people are suggesting here, you could call Wait() or Result on the resulting task in your synchronous method, but then you end up with a blocking call in that method, which sort of defeats the purpose of async.
If you really can't make your method async and you don't want to lock up the synchronous method, then you're going to have to use a callback method by passing it as parameter to the ContinueWith() method on task.
Inspired by some of the other answers, I created the following simple helper methods:
public static TResult RunSync<TResult>(Func<Task<TResult>> method)
{
var task = method();
return task.GetAwaiter().GetResult();
}
public static void RunSync(Func<Task> method)
{
var task = method();
task.GetAwaiter().GetResult();
}
They can be called as follows (depending on whether you are returning a value or not):
RunSync(() => Foo());
var result = RunSync(() => FooWithResult());
Note that the signature in the original question public async void Foo() is incorrect. It should be public async Task Foo() as you should return Task not void for async methods that don't return a value (yes, there are some rare exceptions).
Stephen Cleary's Answer;
That approach shouldn't cause a deadlock (assuming that
ProblemMethodAsync doesn't send updates to the UI thread or anything
like that). It does assume that ProblemMethodAsync can be called on a
thread pool thread, which is not always the case.
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
And here is the approach;
The Thread Pool Hack A similar approach to the Blocking Hack is to
offload the asynchronous work to the thread pool, then block on the
resulting task. The code using this hack would look like the code
shown in Figure 7.
Figure 7 Code for the Thread Pool Hack
C#
public sealed class WebDataService : IDataService
{
public string Get(int id)
{
return Task.Run(() => GetAsync(id)).GetAwaiter().GetResult();
}
public async Task<string> GetAsync(int id)
{
using (var client = new WebClient())
return await client.DownloadStringTaskAsync(
"https://www.example.com/api/values/" + id);
}
}
The call to Task.Run executes the asynchronous method on a thread pool
thread. Here it will run without a context, thus avoiding the
deadlock. One of the problems with this approach is the asynchronous
method can’t depend on executing within a specific context. So, it
can’t use UI elements or the ASP.NET HttpContext.Current.
Here is the simplest solution. I saw it somewhere on the Internet, I didn't remember where, but I have been using it successfully. It will not deadlock the calling thread.
void SynchronousFunction()
{
Task.Run(Foo).Wait();
}
string SynchronousFunctionReturnsString()
{
return Task.Run(Foo).Result;
}
string SynchronousFunctionReturnsStringWithParam(int id)
{
return Task.Run(() => Foo(id)).Result;
}
After hours of trying different methods, with more or less success, this is what I ended with. It doesn't end in a deadlock while getting result and it also gets and throws the original exception and not the wrapped one.
private ReturnType RunSync()
{
var task = Task.Run(async () => await myMethodAsync(agency));
if (task.IsFaulted && task.Exception != null)
{
throw task.Exception;
}
return task.Result;
}
You can now use source generators to create a sync version of your method using Sync Method Generator library (nuget).
Use it as follows:
[Zomp.SyncMethodGenerator.CreateSyncVersion]
public async void FooAsync()
Which will generate Foo method which you can call synchronously.
Those windows async methods have a nifty little method called AsTask(). You can use this to have the method return itself as a task so that you can manually call Wait() on it.
For example, on a Windows Phone 8 Silverlight application, you can do the following:
private void DeleteSynchronous(string path)
{
StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
Task t = localFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask();
t.Wait();
}
private void FunctionThatNeedsToBeSynchronous()
{
// Do some work here
// ....
// Delete something in storage synchronously
DeleteSynchronous("pathGoesHere");
// Do other work here
// .....
}
Hope this helps!
If you want to run it Sync
MethodAsync().RunSynchronously()
In contrast to Task.Wait() or Task.Result, await’ing a Task in C# 5 prevents the thread which executes the wait from lying fallow. Instead, the method using the await keyword needs to be async so that the call of await just makes the method to return a new task which represents the execution of the async method.
But when the await’ed Task completes before the async method has received CPU time again, the await recognizes the Task as finished and thus the async method will return the Task object only at a later time. In some cases this would be later than acceptable because it probably is a common mistake that a developer assumes the await’ing always defers the subsequent statements in his async method.
The mistaken async method’s structure could look like the following:
async Task doSthAsync()
{
var a = await getSthAsync();
// perform a long operation
}
Then sometimes doSthAsync() will return the Task only after a long time.
I know it should rather be written like this:
async Task doSthAsync()
{
var a = await getSthAsync();
await Task.Run(() =>
{
// perform a long operation
};
}
... or that:
async Task doSthAsync()
{
var a = await getSthAsync();
await Task.Yield();
// perform a long operation
}
But I do not find the last two patterns pretty and want to prevent the mistake to occur. I am developing a framework which provides getSthAsync and the first structure shall be common. So getSthAsync should return an Awaitable which always yields like the YieldAwaitable returned by Task.Yield() does.
Unfortunately most features provided by the Task Parallel Library like Task.WhenAll(IEnumerable<Task> tasks) only operate on Tasks so the result of getSthAsync should be a Task.
So is it possible to return a Task which always yields?
First of all, the consumer of an async method shouldn't assume it will "yield" as that's nothing to do with it being async. If the consumer needs to make sure there's an offload to another thread they should use Task.Run to enforce that.
Second of all, I don't see how using Task.Run, or Task.Yield is problematic as it's used inside an async method which returns a Task and not a YieldAwaitable.
If you want to create a Task that behaves like YieldAwaitable you can just use Task.Yield inside an async method:
async Task Yield()
{
await Task.Yield();
}
Edit:
As was mentioned in the comments, this has a race condition where it may not always yield. This race condition is inherent with how Task and TaskAwaiter are implemented. To avoid that you can create your own Task and TaskAwaiter:
public class YieldTask : Task
{
public YieldTask() : base(() => {})
{
Start(TaskScheduler.Default);
}
public new TaskAwaiterWrapper GetAwaiter() => new TaskAwaiterWrapper(base.GetAwaiter());
}
public struct TaskAwaiterWrapper : INotifyCompletion
{
private TaskAwaiter _taskAwaiter;
public TaskAwaiterWrapper(TaskAwaiter taskAwaiter)
{
_taskAwaiter = taskAwaiter;
}
public bool IsCompleted => false;
public void OnCompleted(Action continuation) => _taskAwaiter.OnCompleted(continuation);
public void GetResult() => _taskAwaiter.GetResult();
}
This will create a task that always yields because IsCompleted always returns false. It can be used like this:
public static readonly YieldTask YieldTask = new YieldTask();
private static async Task MainAsync()
{
await YieldTask;
// something
}
Note: I highly discourage anyone from actually doing this kind of thing.
Here is a polished version of i3arnon's YieldTask:
public class YieldTask : Task
{
public YieldTask() : base(() => { },
TaskCreationOptions.RunContinuationsAsynchronously)
=> RunSynchronously();
public new YieldAwaitable.YieldAwaiter GetAwaiter()
=> default;
public new YieldAwaitable ConfigureAwait(bool continueOnCapturedContext)
{
if (!continueOnCapturedContext) throw new NotSupportedException();
return default;
}
}
The YieldTask is immediately completed upon creation, but its awaiter says otherwise. The GetAwaiter().IsCompleted always returns false. This mischief makes the await operator to trigger the desirable asynchronous switch, every time it awaits this task. Actually creating multiple YieldTask instances is redundant. A singleton would work just as well.
There is a problem with this approach though. The underlying methods of the Task class are not virtual, and hiding them with the new modifier means that polymorphism doesn't work. If you store a YieldTask instance to a Task variable, you'll get the default task behavior. This is a considerable drawback for my use case, but I can't see any solution around it.