Imagine there is a method
async static System.Threading.Tasks.Task Do()
{
//do sth
await Task.Delay(10000);
}
now , when I call the method with
Do();
Does it make a new thread ? or I have to create a thread as below
Task.Factory.StartNew(() => Do());
Do doesn't create a thread.
The part before the await runs on the calling thread and there's no thread at all during the delay. Task.Delay uses a timer internally which doesn't hold up a thread throughout the operation.
When the delay completes a thread from the ThreadPool is used to continue executing the method.
About Task.Factory.StartNew, why would you want to create a thread? If you need to offload a CPU intensive method to the thread pool then it's fine (although Task.Run is preferable) but if you don't then simply call Do and await the result:
await Do();
If you insist on creating a new thread for Do you need to use TaskCreationOptions.LongRunning:
Task.Factory.StartNew(() => Do(), TaskCreationOptions.LongRunning);
But since Do is an async method that releases the thread when it reaches the first await this doesn't make any sense as I've explained on my blog: LongRunning Is Useless For Task.Run With async-await
All my case is this :
public void Start(Action action)
{
Token = CancellationToken.None;
IsRunning = true;
Task.Factory.StartNew(() => Do(action), TaskCreationOptions.LongRunning);
}
async Task Do(Action action)
{
while (IsRunning)
{
action();
await Task.Delay(Interval, Token);
}
}
then I call Start() with five different actions with different intervals.
Is this Correct to use ?
Related
I recently came across some code which confused me heavily, I have always thought that you must use threads or Async tasks, not mix and match between them,
public async Task DoWork()
{
Task.Delay(1000);
}
Now I saw code calling this like so:
public void Main()
{
var thread = new Thread(async () => { await DoWorkAync(); })
{
Priority = ThreadPriority.Highest,
IsBackground = true
};
// Start thread
proccessThread.Start();
}
Now this magically seemed to NOT create a thread each time it was run, it seemed to be using the ThreadPool.
now what I am struggling to understand is the difference between the above and:
public void Main()
{
var task = Task.Run(DoWorkASync);
}
From my testing, it seems that C# Thread has a different functionality when passing in an Async Expression vs the standard method on which to run>
This construct:
var thread = new Thread(async () => { await DoWorkAync(); });
// Start thread
proccessThread.Start();
Calls Thread constructor overload accepting ThreadStart delegate, and ThreadStart delegate is () => void. So you have this:
var thread = new Thread(StuffYourThreadExecutes);
thread.Start();
static async void StuffYourThreadExecutes() {
await DoWorkAsync();
}
So you start new thread and it runs the code until first asynchronous operation begins. Then thread exists. After that first asynchronous operation completes - the rest executes on whatever thread task scheduler providers (usually thread pool thread). Any exceptions which happen during this process cannot be observed.
For example if DoWorkAsync is something like:
static async Task DoWorkAsync(){
await Task.Delay(1000);
}
Then thread starts and almost immediately exits, doing nothing useful.
Task.Run, when passing async delegate there, does what is stated in docs:
Queues the specified work to run on the thread pool and returns a
proxy for the task
So whole operation just runs on thread pool thread without creating threads for nothing. You can observe exceptions by awaiting task returned by Task.Run.
I'm routinely getting what I think is a deadlock in my C# code that makes heavy use of async-await. I sometimes get it on this line of code:
await context.SaveChangesAsync();
The thread just blocks indefinitely.
I'm not blocking synchronously anywhere in the code. I use async-await all the way to the top where I initialize a new background thread like so from the entry method which is synchronous:
var threads =
new ThreadStart[]
{
async () => await Run(InitiationDelegate),
}.Select(ts =>
new Thread(ts) { IsBackground = true }
);
foreach(var thread in threads)
{
thread.Start();
}
and Run has the following signature:
async Task Run(Func<Task> action)
I'm not sure where the deadlocks could be coming from?
I'm struggling to understand what's happening in this simple program.
In the example below I have a task factory that uses the LimitedConcurrencyLevelTaskScheduler from ParallelExtensionsExtras with maxDegreeOfParallelism set to 2.
I then start 2 tasks that each call an async method (e.g. an async Http request), then gets the awaiter and the result of the completed task.
The problem seem to be that Task.Delay(2000) never completes. If I set maxDegreeOfParallelism to 3 (or greater) it completes. But with maxDegreeOfParallelism = 2 (or less) my guess is that there is no thread available to complete the task. Why is that?
It seems to be related to async/await since if I remove it and simply do Task.Delay(2000).GetAwaiter().GetResult() in DoWork it works perfectly. Does async/await somehow use the parent task's task scheduler, or how is it connected?
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Threading.Tasks.Schedulers;
namespace LimitedConcurrency
{
class Program
{
static void Main(string[] args)
{
var test = new TaskSchedulerTest();
test.Run();
}
}
class TaskSchedulerTest
{
public void Run()
{
var scheduler = new LimitedConcurrencyLevelTaskScheduler(2);
var taskFactory = new TaskFactory(scheduler);
var tasks = Enumerable.Range(1, 2).Select(id => taskFactory.StartNew(() => DoWork(id)));
Task.WaitAll(tasks.ToArray());
}
private void DoWork(int id)
{
Console.WriteLine($"Starting Work {id}");
HttpClientGetAsync().GetAwaiter().GetResult();
Console.WriteLine($"Finished Work {id}");
}
async Task HttpClientGetAsync()
{
await Task.Delay(2000);
}
}
}
Thanks in advance for any help
await by default captures the current context and uses that to resume the async method. This context is SynchronizationContext.Current, unless it is null, in which case it is TaskScheduler.Current.
In this case, await is capturing the LimitedConcurrencyLevelTaskScheduler used to execute DoWork. So, after starting the Task.Delay both times, both of those threads are blocked (due to the GetAwaiter().GetResult()). When the Task.Delay completes, the await schedules the remainder of the HttpClientGetAsync method to its context. However, the context will not run it since it already has 2 threads.
So you end up with threads blocked in the context until their async methods complete, but the async methods cannot complete until there is a free thread in the context; thus a deadlock. Very similar to the standard "don't block on async code" style of deadlock, just with n threads instead of one.
Clarifications:
The problem seem to be that Task.Delay(2000) never completes.
Task.Delay is completing, but the await cannot continue executing the async method.
If I set maxDegreeOfParallelism to 3 (or greater) it completes. But with maxDegreeOfParallelism = 2 (or less) my guess is that there is no thread available to complete the task. Why is that?
There are plenty of threads available. But the LimitedConcurrencyTaskScheduler only allows 2 threads at a time to run in its context.
It seems to be related to async/await since if I remove it and simply do Task.Delay(2000).GetAwaiter().GetResult() in DoWork it works perfectly.
Yes; it's the await that is capturing the context. Task.Delay does not capture a context internally, so it can complete without needing to enter the LimitedConcurrencyTaskScheduler.
Solution:
Task schedulers in general do not work very well with asynchronous code. This is because task schedulers were designed for Parallel Tasks rather than asynchronous tasks. So they only apply when code is running (or blocked). In this case, LimitedConcurrencyLevelTaskScheduler only "counts" code that's running; if you have a method that's doing an await, it won't "count" against that concurrency limit.
So, your code has ended up in a situation where it has the sync-over-async antipattern, probably because someone was trying to avoid the problem of await not working as expected with limited concurrency task schedulers. This sync-over-async antipattern has then caused the deadlock problem.
Now, you could add in more hacks by using ConfigureAwait(false) everywhere and continue blocking on asynchronous code, or you could fix it better.
A more proper fix would be to do asynchronous throttling. Toss out the LimitedConcurrencyLevelTaskScheduler completely; concurrency-limiting task schedulers only work with synchronous code, and your code is asynchronous. You can do asynchronous throttling using SemaphoreSlim, as such:
class TaskSchedulerTest
{
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(2);
public async Task RunAsync()
{
var tasks = Enumerable.Range(1, 2).Select(id => DoWorkAsync(id));
await Task.WhenAll(tasks);
}
private async Task DoWorkAsync(int id)
{
await _mutex.WaitAsync();
try
{
Console.WriteLine($"Starting Work {id}");
await HttpClientGetAsync();
Console.WriteLine($"Finished Work {id}");
}
finally
{
_mutex.Release();
}
}
async Task HttpClientGetAsync()
{
await Task.Delay(2000);
}
}
I think you are encountering a sync deadlock. You are waiting for a thread to complete that is waiting for your thread to complete. Never going to happen. If you make your DoWork method async so you can await the HttpClientGetAsync() call, and you'll avoid the deadlock.
using MassTransit.Util;
using System;
using System.Linq;
using System.Threading.Tasks;
//using System.Threading.Tasks.Schedulers;
namespace LimitedConcurrency
{
class Program
{
static void Main(string[] args)
{
var test = new TaskSchedulerTest();
test.Run();
}
}
class TaskSchedulerTest
{
public void Run()
{
var scheduler = new LimitedConcurrencyLevelTaskScheduler(2);
var taskFactory = new TaskFactory(scheduler);
var tasks = Enumerable.Range(1, 2).Select(id => taskFactory.StartNew(() => DoWork(id)));
Task.WaitAll(tasks.ToArray());
}
private async Task DoWork(int id)
{
Console.WriteLine($"Starting Work {id}");
await HttpClientGetAsync();
Console.WriteLine($"Finished Work {id}");
}
async Task HttpClientGetAsync()
{
await Task.Delay(2000);
}
}
}
https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d
TLDR never call .result, which I'm sure .GetResult(); was doing
I would like to try using background workers. I am interesting to use async/await.
I have 3 parallel tasks.
private async void RunDownloadsAsync()
{
Task taskDelay1 = Task.Run(() => Task.Delay(10000));
Task taskDelay2 = Task.Run(() => Task.Delay(15000));
Task taskDelay3 = Task.Run(() => Task.Delay(20000));
await ???
}
Let's assume each of these Tasks changes the value of 3 Labels (label1, label2, label3) on a form when they are done. i.e. all labels are set to "Pending" and when each task is done, their corresponding label value will change to "Finished".
I would like to await each task accordingly so that they each do their own corresponding task. That means if taskDelay1's job is finished set label1's text to "Finished" while taskDelay2 and taskDelay3 are still pending. But if I put 3 awaits there, the program will wait for all of them to finish their task and then continue the rest of the code.
How can I continue the rest of the code in which each label only waits for its own task to finish and then decide what to do?
You should use Task.WhenAll method to wait for all of them and ContinueWith to execute operation when the task is completed.
Task taskDelay1 = Task.Run(() => Task.Delay(10000)).ContinueWith(t => SetLabel());
Task taskDelay2 = Task.Run(() => Task.Delay(15000)).ContinueWith(t => SetLabel());
Task taskDelay3 = Task.Run(() => Task.Delay(20000)).ContinueWith(t => SetLabel());
await Task.WhenAll(taskDelay1, taskDelay2, taskDelay3);
There's an important distinction between parallel and concurrent. Parallel refers to using multiple threads to do CPU-bound work. Parallelism is one form of concurrency, but not the only one. Another form of concurrency is asynchrony, which can do non-CPU-bound work without introducing multiple threads.
In your case, "downloads" imply an I/O-bound operation, which should be done with asynchrony and not parallelism. Check out the HttpClient class for asynchronous downloads. You can use Task.WhenAll for asynchronous concurrency:
private async Task RunDownload1Async()
{
label1.Text = "Pending";
await Task.Delay(10000);
label1.Text = "Finished";
}
private async Task RunDownload2Async()
{
label2.Text = "Pending";
await Task.Delay(15000);
label2.Text = "Finished";
}
private async Task RunDownload3Async()
{
label3.Text = "Pending";
await Task.Delay(20000);
label3.Text = "Finished";
}
private async Task RunDownloadsAsync()
{
await Task.WhenAll(RunDownload1Async(), RunDownload2Async(), RunDownload3Async());
}
This approach avoids creating unnecessary threads and also does not use outdated techniques (ContinueWith, Dispatcher.Invoke). That said, it's not perfect, since the GUI logic is mixed with the operational logic in the RunDownloadNAsync methods. A better approach would be to use IProgress<T> and Progress<T> for updating the UI.
You can use ContinueWith to execute an action after the Task is completed. Note that you might need to call Dispatcher.Invoke in the expression in the ContinueWith to prevent cross-thread calls.
Here is a sample from a WPF application, using Dispatcher.Invoke:
private static async void Async()
{
Task taskDelay1 = Task.Run(() => Task.Delay(1000))
.ContinueWith(x => Dispatcher.Invoke(() => this.label1.Content = "One done"));
Task taskDelay2 = Task.Run(() => Task.Delay(1500))
.ContinueWith(x => Dispatcher.Invoke(() => this.label2.Content = "Two done"));
Task taskDelay3 = Task.Run(() => Task.Delay(2000))
.ContinueWith(x => Dispatcher.Invoke(() => this.label3.Content = "Three done"));
await Task.WhenAll(taskDelay1, taskDelay2, taskDelay3);
}
As Dirk suggested, an alternative to calling Dispatcher.Invoke would be to use a task scheduler, e.g. TaskScheduler.FromCurrentSynchronizationContext() if you're on the UI thread.
Apologies in advance if this question is opinion-based. The lack of Task.Yield version which wouldn't capture the execution context was already discussed here. Apparently, this feature was present in some form in early versions of Async CTP but was removed because it could easily be misused.
IMO, such feature could be as easily misused as Task.Run itself. Here's what I mean. Imagine there's an awaitable SwitchContext.Yield API which schedules the continuation on ThreadPool, so the execution will always continues on a thread different from the calling thread. I could have used it in the following code, which starts some CPU-bound work from a UI thread. I would consider it a convenient way of continuing the CPU-bound work on a pool thread:
class Worker
{
static void Log(string format, params object[] args)
{
Debug.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, String.Format(format, args));
}
public async Task UIAction()
{
// UI Thread
Log("UIAction");
// start the CPU-bound work
var cts = new CancellationTokenSource(5000);
var workTask = DoWorkAsync(cts.Token);
// possibly await for some IO-bound work
await Task.Delay(1000);
Log("after Task.Delay");
// finally, get the result of the CPU-bound work
int c = await workTask;
Log("Result: {0}", c);
}
async Task<int> DoWorkAsync(CancellationToken ct)
{
// start on the UI thread
Log("DoWorkAsync");
// switch to a pool thread and yield back to the UI thread
await SwitchContext.Yield();
Log("after SwitchContext.Yield");
// continue on a pool thread
int c = 0;
while (!ct.IsCancellationRequested)
{
// do some CPU-bound work on a pool thread: counting cycles :)
c++;
// and use async/await too
await Task.Delay(50);
}
return c;
}
}
Now, without SwitchContext.Yield, DoWorkAsync would look like below. It adds some extra level of complexity in form of async delegate and task nesting:
async Task<int> DoWorkAsync(CancellationToken ct)
{
// start on the UI thread
Log("DoWorkAsync");
// Have to use async delegate
// Task.Run uwraps the inner Task<int> task
return await Task.Run(async () =>
{
// continue on a pool thread
Log("after Task.Yield");
int c = 0;
while (!ct.IsCancellationRequested)
{
// do some CPU-bound work on a pool thread: counting cycles :)
c++;
// and use async/await too
await Task.Delay(50);
}
return c;
});
}
That said, implementing SwitchContext.Yield may actually be quite simple and (I dare to say) efficient:
public static class SwitchContext
{
public static Awaiter Yield() { return new Awaiter(); }
public struct Awaiter : System.Runtime.CompilerServices.INotifyCompletion
{
public Awaiter GetAwaiter() { return this; }
public bool IsCompleted { get { return false; } }
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem((state) => ((Action)state)(), continuation);
}
public void GetResult() { }
}
}
So, my question is, why should I prefer the second version of DoWorkAsync over the first one, and why would using SwitchContext.Yield be considered a bad practice?
You don't have to put the Task.Run in DoWorkAsync. Consider this option:
public async Task UIAction()
{
// UI Thread
Log("UIAction");
// start the CPU-bound work
var cts = new CancellationTokenSource(5000);
var workTask = Task.Run(() => DoWorkAsync(cts.Token));
// possibly await for some IO-bound work
await Task.Delay(1000);
Log("after Task.Delay");
// finally, get the result of the CPU-bound work
int c = await workTask;
Log("Result: {0}", c);
}
This results in code with much clearer intent. DoWorkAsync is a naturally synchronous method, so it has a synchronous signature. DoWorkAsync neither knows nor cares about the UI. The UIAction, which does care about the UI thread, pushes off the work onto a background thread using Task.Run.
As a general rule, try to "push" any Task.Run calls up out of your library methods as much as possible.