I have some objects that start long-running background work on construction and would like to await their completion on IAsyncDisposable.
I would like to run this work on the thread pool. I am trying to figure out the safest way to do this while avoiding deadlocks. I cannot figure out how to use JoinableTaskFactory and/or JoinableContext to do this.
using Microsoft.VisualStudio.Threading;
public class Worker : System.IAsyncDisposable
{
private CancellationTokenSource _cts;
private Task _work;
private JoinableTask _workSafe;
public Worker()
{
_cts = new();
// goes on the thread pool. But no way to join/await on that thread later?
_work = Task.Run(() => DoWorkAsync(_cts.Token));
// using the library I can await on same thread later. But it
// executes on the same thread as the constructor (undesirable)
// according to the documentation
var ctx = new JoinableTaskContext();
_workSafe = ctx.Factory.RunAsync(() => DoWorkAsync(_cts.Token), JoinableTaskCreationOptions.LongRunning);
}
private async Task DoWorkAsync(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
await Task.Delay(50);
}
}
public async ValueTask DisposeAsync()
{
_cts.Cancel();
// VS says this is unsafe
await _work;
// But this is safe
await _workSafe;
_cts.Dispose();
}
}
Related
I am starting a Thread where an await Task.Run can be invoked.
After starting a Thread with the ThreadStart.Start method, why does the await Task.Run terminate the Thread and Task.Run does not?
Here is some code as an example:
public async Task Task1()
{
if (awaitRunTask)
{
await Task.Run(async () =>
{
await Test();
}
);
}
else
{
Task.Run(async () =>
{
await Test();
}
);
}
}
In the above example, if a Thread invokes the Task1 method, and awaitRunTask = true, the Thread terminates. If awaitRunTask = false, the Thread does not terminate.
When I say terminate, the Thread does not complete correctly and the method where the ThreadStart.Start is invoked returns. This happens at the await Test() code.
Why is this and if I want to await a Task.Run on a Thread, is there a way to do this?
EDIT
Here is some code to show a more detailed example:
public class ThreadExample
{
public bool awaitRunTask;
public string value;
private async Task StartThreadAsync()
{
var method = this.GetType().GetMethod("RunTasksAsync");
ThreadStart threadStart;
threadStart = async () =>
{
await InvokeAsync(method, this, null);
};
var thread = new Thread(threadStart);
thread.Start();
thread.Join();
}
public async Task RunTasksAsync()
{
await Task1Async();
Task2();
}
private async Task Task1Async()
{
if (awaitRunTask)
{
await Task.Run(async () =>
{
await TestAsync();
}
);
}
else
{
Task.Run(async () =>
{
await TestAsync();
}
);
}
}
private void Task2()
{
value = "valid";
}
private async Task TestAsync()
{
await Task.Delay(1000);
}
private async Task InvokeAsync(MethodInfo method, object instance, object[] parameters)
{
dynamic awaitable = method.Invoke(instance, parameters);
await awaitable;
}
public async Task ValueIsCorrectAsync()
{
value = "not valid";
awaitRunTask = false;
await StartThreadAsync();
var isCorrect = (value == "valid");
}
public async Task ValueIsNotCorrectAsync()
{
value = "not valid";
awaitRunTask = true;
await StartThreadAsync();
var isCorrect = (value == "valid");
}
}
The ValueIsCorrectAsync method works correctly as the Task2 method sets the value field.
The ValueIsNotCorrectAsync method does not work correctly as the await Task.Run in the Task1Async method interferes with the Thread. The StartThreadAsync method returns before the Task2 method sets the value field.
The only difference between the two methods, is the value of awaitRunTask.
How should I change my code such that the value field is set correctly when awaitRunTask = true?
EDIT3
If the await Task.Delay(1000); is commented out in the TestAsync method, the code works for both awaitRunTask = true and awaitRunTask = false;
Can someone please explain to me why? I need to know why because the TestAsync method needs to be able to run asynchronous code.
Why is this?
As I explain on my blog, await here is actually returning to its caller. So the Task1 method returns an incomplete task to its caller, presumably the thread's main method, which presumably is async void. When the thread's main method returns (due to it's await), the thread exits.
The core of the problem is that the Thread type doesn't understand or work naturally with asynchronous code. Thread is really a very low-level building block at this point and is best avoided in modern code. There are very few scenarios where it can't be replaced with Task.Run.
if I want to await a Task.Run on a Thread, is there a way to do this?
The easiest solution is to get rid of the legacy thread completely; replace it with Task.Run.
Otherwise, you need the thread to block. If the continuations can run on thread pool threads, then you can just block directly (e.g., GetAwaiter().GetResult()). If the continuations need to run on that thread, then use AsyncContext from my AsyncEx library.
Here's a minimal version of your sample code:
async Task Main()
{
var te = new ThreadExample();
await te.StartThreadAsync(false);
await te.StartThreadAsync(true);
}
public class ThreadExample
{
public string value;
public async Task StartThreadAsync(bool awaitRunTask)
{
value = "not valid";
var thread = new Thread(() => Task1Async(awaitRunTask));
thread.Start();
thread.Join();
var isCorrect = (value == "valid");
Console.WriteLine(isCorrect);
}
private async Task Task1Async(bool awaitRunTask)
{
if (awaitRunTask)
{
await Task.Run(async () => await Task.Delay(1000));
}
value = "valid";
}
}
This outputs:
True
False
The thread that enters Task1Async executes the line value = "valid" when awaitRunTask == false, but when it's true it hits the await Task.Run and, because of the async state machine, the thread returns to the caller at this point and executes the thread.Join().
Effectively you've created an extreme race condition.
Recently, i had a requirement to queue async tasks and i was introduced to BlockingCollection in this link
Queuing asynchronous task in C#
It worked and i'm having a slight change in requirement and need your guidance. I'm using the BlockingCollection as in #Stephen Cleary answer
This is the BlockingCollection from that link
public sealed class ExecutionQueue
{
//private readonly BlockingCollection<Func<Task>> _queue = new BlockingCollection<Func<Task>>();//commented this
private readonly BlockingCollection<Task> _queue = new BlockingCollection<Task>();
public ExecutionQueue() => Complete = Task.Run(() => ProcessQueueAsync());
public Task Completion { get; }
public void Complete() => _queue.CompleteAdding();
private async Task ProcessQueueAsync()
{
foreach (var value in _queue.GetConsumingEnumerable())
await value();
}
}
//public Task Run(Func<Task> lambda)
public Task Run(<Task> lambda)
{
var tcs = new TaskCompletionSource<object>();
_queue.Add(lamda);
return tcs.Task;
}
I need to queue certain DataBase tasks which is within a regular void method. I may not be able to change the signature of this method. How do i do them?
public static ExecutionQueue taskQueue = new ExecutionQueue();
private void SaveValesToDB(...)
{
var item = GetID(...);
...
taskQueue.Run(Task.Run(() =>
{
DBInstance.DBSaveValue1(...); // is it correct to wrap with Task.Run and add to queue? it should be queued and run asynchronously
});
...
}
We save and retrieve data from DB on and off. So, when we queue a DB call that is returning something like a getter, we want to ensure that until we receive the return value we don't process other items that are queued.
private void SaveValesToDB(...)
{
...
taskQueue.Run(Task.Run(() =>
{
DBInstance.DBSaveValue1(...); // is this correct? it should be queued and run asynchronously
});
...
taskQueue.Run(Task.Run(() =>
{
var result1 = DBInstance.DBGetValue2(...); // should be queued and run asynchronously;
LogData(result1);// not a DB call but believe it should be wrapped in here for the result1, correct?
});
/*so in above Task.Run, i want to ensure that until i receive result1
i don't process other items in the queue even
if they are added. how can i do that ?
The main thread should continue. */
...
var result 2 = DBInstance.DBGetValue3(...); // should be queued and run asynchronously
UpdateAdvancedLod(result1 +" "+result2);// here, should i block main thread until i get result1 ?
}
How to handle errors?
Please, guide me.
Edited:
if using Func<Task> in public Task Run(Func<Task> lambda) then is the below correct?
taskQueue.Run(async () =>
{
await Task.Run(() =>
{
DBInstance.DBSaveValue1(...);//is this correct
});
}
);
You could add this method to Stephen Cleary's ExecutionQueue class:
public Task Run(Action action)
{
return Run(() => Task.Run(action));
}
This is an overload of the existing public Task Run(Func<Task> lambda) method. This one delegates the execution of the supplied action to a ThreadPool thread.
Usage example:
var id = GetID();
var task = taskQueue.Run(() => DBInstance.DBSaveValue1(id));
await task; // Optional
Update: To propagate error notifications to the main thread, you could enhance the ExecutionQueue class with an Error event, which would be invoked in the captured context (captured at the time that the instance was created).
private readonly SynchronizationContext _capturedContext;
public event EventHandler<Exception> Error;
public ExecutionQueue() // Constructor
{
_capturedContext = SynchronizationContext.Current ?? new SynchronizationContext();
Completion = Task.Run(() => ProcessQueueAsync());
}
private void OnError(Exception ex)
{
var handler = Error; if (handler == null) return;
_capturedContext.Post(_ => handler.Invoke(this, ex), null);
}
The OnError should be called from inside the catch (Exception ex) block. This will work with Windows Forms apps and WPF apps, because their UI thread is equipped with a SynchronizationContext. It will not work with a Console app because there is no SynchronizationContext there (the Error event
will be raised in a random ThreadPool thread).
I have an event in my WPF .NET 4.5 application that can be triggered up to 10 times per second, the event is computing data that is not required that often so I'm looking for a way to remove unnecessary computation stress and call the method if event wasn't triggered for 3 seconds. I thought about using async functionality like that:
private async Task DoWork()
{
await Task.Delay(3000);
...
}
private void Event()
{
Task.Run(() => DoWork());
}
I'm not really sure about how to elegantly handle disposing of unwanted Tasks, ie. when user trigger the event, every task that was created by that event should be terminated and it should be as fast as possible. I've tried with CancellationToken but I'm not sure this is the right approach for my case
You can use CancellationTokenSource to let the code inside task know that it is canceled:
private CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource ();
Let's change:
private async Task DoWork()
{
await Task.Delay(3000);
...
}
To:
private async Task DoWork(int timeout = 3000)
{
await Task.Delay(timeout, CancellationTokenSource.Token);
if(!CancellationTokenSource.Token.IsCancellationRequested)
{
...
}
}
Now we can cancel our task if required:
CancellationTokenSource.Cancel();
Task.Delay will observe the CancellationToken and if it is in a Canceled state it will abort the task execution. Later in code we check whether we need to do anything or not.
That was my solution based on #Fabjan answer.
private static void RunSingleTask(ref CancellationTokenSource cts, int delay, Action func)
{
if (cts != null)
{
cts.Cancel();
cts.Dispose();
}
cts = new CancellationTokenSource();
var token = cts.Token;
Task.Run(async () =>
{
try
{
await Task.Delay(delay, token);
}
catch (TaskCanceledException)
{
return;
}
await Application.Current.Dispatcher.BeginInvoke(func);
});
}
Essentially, what I'm doing is creating a web server to handle an API call, and then when it's done continue the method execution, so essentially:
new WebServer(myAutoResetEvent);
myAutoResetEvent.WaitOne();
However, this blocks the thread until then. Is there any way to make this async? Is it fine just to wrap it in a await Task.Run() call, i.e. await Task.Run(() => myAutoResetEvent.WaitOne())?
Thanks!
Normally, the WebServer ctor should not do anything interesting. There should be a Task WebServer.RunAsync function that runs the server. You can then use the resulting task to synchronize and coordinate.
If you don't want that you can use a TaskCompletionSource<object> as a one-shot async-ready event.
I believe the ThreadPool class has a way to efficiently wait for a WaitHandle to be set but that's a worse solution.
You should not block ThreadPool threads, this is a quick way to lead to ThreadPool starvation, instead there is a provided method to asynchronously wait for WaitHandle instances, this is called ThreadPool.RegisterWaitForSingleObject.
By using ThreadPool.RegisterWaitForSingleObject a callback is registered to be invoked when the WaitHandle is available, unfortunately this is not async/await compatible out of the box, a full implementation which makes this async/await compatible is as follows:
public static class WaitHandleExtensions
{
public static Task WaitOneAsync(this WaitHandle waitHandle, CancellationToken cancellationToken)
{
return WaitOneAsync(waitHandle, Timeout.Infinite, cancellationToken);
}
public static async Task<bool> WaitOneAsync(this WaitHandle waitHandle, int timeout, CancellationToken cancellationToken)
{
// A Mutex can't use RegisterWaitForSingleObject as a Mutex requires the wait and release to be on the same thread
// but RegisterWaitForSingleObject acquires the Mutex on a ThreadPool thread.
if (waitHandle is Mutex)
throw new ArgumentException(StringResources.MutexMayNotBeUsedWithWaitOneAsyncAsThreadIdentityIsEnforced, nameof(waitHandle));
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource<bool>();
var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle, OnWaitOrTimerCallback, tcs, timeout, true);
var cancellationCallback = BuildCancellationCallback(rwh, tcs);
using (cancellationToken.Register(cancellationCallback))
{
try
{
return await tcs.Task.ConfigureAwait(false);
}
finally
{
rwh.Unregister(null);
}
}
}
private static Action BuildCancellationCallback(RegisteredWaitHandle rwh, TaskCompletionSource<bool> tcs)
{
return () =>
{
if (rwh.Unregister(null))
{
tcs.SetCanceled();
}
};
}
private static void OnWaitOrTimerCallback(object state, bool timedOut)
{
var taskCompletionSource = (TaskCompletionSource<bool>)state;
taskCompletionSource.SetResult(!timedOut);
}
}
The only limitation is that this cannot be used with a Mutex.
This can be used like so:
await myAutoResetEvent.WaitOneAsync(cancellationToken).ConfigureAwait(false);
Another approach to consider would be to use HttpSelfHostServer (System.Web.Http.SelfHost.dll) and leaving all of the threading details to its implementation.
var config = new HttpSelfHostConfiguration("http://localhost:9999");
var tcs = new TaskCompletionSource<Uri>();
using (var server = new HttpSelfHostServer(config, new MessageHandler(tcs)))
{
await server.OpenAsync();
await tcs.Task;
await server.CloseAsync();
}
return tcs.Task.Result;
class MessageHandler : HttpMessageHandler
{
private readonly TaskCompletionSource<Uri> _task;
public MessageHandler(TaskCompletionSource<Uri> task)
{
_task = task;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
_task.SetResult(request.RequestUri);
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
}
}
I'm curious about how the flow of async works across the stack. When reading about async in C#, you will continually read some version of the following:
If the task we are awaiting has not yet completed then sign up the
rest of this method as the continuation of that task, and then return
to your caller immediately; the task will invoke the continuation when
it completes.
It's the return to your caller immediately part that confuses me. In the below example, assuming something calls MethodOneAsync(), execution hits the await in MethodOneAsync. Does it immediately return to the method that called this? If so, how does MethodTwoAsync ever get executed? Or does it continue down the stack until it detects that it's actually blocked (ie. DoDBWorkAsync()) and then yield the thread?
public static async Task MethodOneAsync()
{
DoSomeWork();
await MethodTwoAsync();
}
public static async Task MethodTwoAsync()
{
DoSomeWork();
await MethodThreeAsync();
}
public static async Task MethodThreeAsync()
{
DoSomeWork();
await DoDBWorkAsync();
}
The part before an await in an async method is executed synchronously. That's the case for all async methods.
Let's assume that instead of await DoDBWorkAsync() we have await Task.Delay(1000).
That means MethodOneAsync starts running, executes DoSomeWork and calls MethodTwoAsync which in turn executes DoSomeWork which calls MethodThreeAsync which again executes DoSomeWork.
Then it calls Task.Delay(1000), gets back an uncompleted Task and awaits it.
That await is logically equivalent to adding a continuation and returning the task back to the caller, which is MethodTwoAsync which does the same and return a Task to the caller and so forth and so forth.
That way when the root delay Task completes all the continuations can run one after the other.
If we make your example a bit more complicated:
public static async Task MethodOneAsync()
{
DoSomeWorkOne();
await MethodTwoAsync();
DoMoreWorkOne();
}
public static async Task MethodTwoAsync()
{
DoSomeWorkTwo();
await MethodThreeAsync();
DoMoreWorkTwo();
}
public static async Task MethodThreeAsync()
{
DoSomeWorkThree();
await Task.Delay(1000);
DoMoreWorkThree();
}
It would be logically similar to doing this with continuations:
public static Task MethodOneAsync()
{
DoSomeWorkOne();
DoSomeWorkTwo();
DoSomeWorkThree();
return Task.Delay(1000).
ContinueWith(_ => DoMoreWorkThree()).
ContinueWith(_ => DoMoreWorkTwo()).
ContinueWith(_ => DoMoreWorkOne());
}
First, let me do a different example so my code further down will make sense
public static async Task MethodOneAsync()
{
DoSomeWork1();
await MethodTwoAsync();
DoOtherWork1();
}
public static async Task MethodTwoAsync()
{
DoSomeWork2();
await MethodThreeAsync();
DoOtherWork2();
}
public static async Task MethodThreeAsync()
{
DoSomeWork3();
await DoDBWorkAsync();
DoOtheWork3();
}
All async await does is turn the above code in to something similar to (but even this is a HUGE simplification) this
public static Task MethodOneAsync()
{
DoSomeWork1();
var syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
var resultTask = MethodTwoAsync();
return resultTask.ContinueWith((task) =>
{
syncContext.Post((state) =>
{
SynchronizationContext.SetSynchronizationContext(syncContext);
DoOtherWork1();
}, null);
});
}
public static Task MethodTwoAsync()
{
DoSomeWork2();
var syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
var resultTask = MethodThreeAsync();
return resultTask.ContinueWith((task) =>
{
syncContext.Post((state) =>
{
SynchronizationContext.SetSynchronizationContext(syncContext);
DoOtherWork2();
}, null);
});
}
public static Task MethodThreeAsync()
{
DoSomeWork3();
var syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
var resultTask = DoDbWorkAsync();
return resultTask.ContinueWith((task) =>
{
syncContext.Post((state) =>
{
SynchronizationContext.SetSynchronizationContext(syncContext);
DoOtherWork3();
}, null);
});
}.
Each await just executes the next layer deeper till it is forced to return a Task, once that happens it starts doing continuations on the tasks when they complete and in that continuation it passes in a delegate representing "the rest of the function" to SynchronizationContext.Post( to get the code scheduled to be executed.
How it is scheduled depends on which SynchronizationContext you are in. In WPF and Winforms it queues it to the message pump, for the default and ASP.NET it queues it on a thread pool worker.
It does return after firing the Task for MethodTwoAsync method (or continues execution, if this Task is immediately done), so the inner Task is executing into SynchronizationContext.Current environment.
And after this Task is done, the .NET state machine return the execution to the point right after the MethodTwoAsync firing, and process the rest of the code.
See more information at MSDN article: