Code below that I try to learn Task class. From the output, I see the main thread and task thread are running at the same time. But i get warning message in async method saying that:
"Warning 1 This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread."
so then is the code below synchronous?
namespace SampleThreadTaskClass
{
class Program
{
static void Main(string[] args)
{
Task task = new Task(ProcessDataAsync);
task.Start();
Console.WriteLine("Enter any key");
string input = Console.ReadLine();
Console.WriteLine("You entered: " + input);
Console.ReadLine();
}
static async void ProcessDataAsync()
{
for (int i = 0; i < 20; i++)
{
Thread.Sleep(500);
Console.WriteLine("processing... " + i);
}
}
}
}
The ProcessDataAsync method is indeed synchronous. It claims to be asynchronous and it is lying in that claim.
You then provide that synchronous method to the constructor for Task which will execute that synchronous method in a thread pool thread (which you shouldn't use by the way; if you want to execute a synchronous method in a thread pool thread you should use Task.Run).
Of course, for your case you don't want to execute this method in a thread pool thread. You're asynchronous operation is just waiting. There's no need to schedule a new thread pool thread to just sit there doing nothing for seconds at a time.
You should make ProcessDataAsync actually be asynchronous, using Task.Delay to create a Task that will complete in a given interval of time, which you can await, and then you can simply call ProcessDataAsync when you want to start your asynchronous method and it will actually be asynchronous.
What you should try is write some code before starting the task, then start the task in an asynchronous method. Then have another async method like ProcessDataAsync use await operator in the first async method. This way you can come to know about the async behavior.
This would help
static async Task ProcessDataAsync()
{
for (int i = 0; i < 20; i++)
{
await Task.Delay(500);
Console.WriteLine("processing... " + i);
}
}
When you change your return type to async, you need to change your call toProcessDataAsyc to this
var task = Task.Run(ProcessDataAsync);
And yes your code is async.
Yes it is. Thread.Sleep() is not asynchronous.
See Explanation: https://msdn.microsoft.com/en-us/library/hh156528.aspx#Example
Try delay instead, then you can add an await in front of it,
await Task.Delay(10000);
Related
Basic overview: program should launch task to parse some array of data and occasionally enqueue tasks to process it one at a time. Test rig have a button an two labels to display debug info. TaskQueue is a class for SemaphoreSlim from this thread
Dispatcher dispath = Application.Current.Dispatcher;
async void Test_Click(s, e)
{
TaskQueue queue = new TaskQueue();
// Blocks thread if SimulateParse does not have await inside
await SimulateParse(queue);
//await Task.Run(() => SimulateParse(queue));
lblStatus2.Content = string.Format("Awaiting queue"));
await queue.WaitAsync(); //this is just SemaphoreSlim.WaitAsync()
lblStatus.Content = string.Format("Ready"));
lblStatus2.Content = string.Format("Ready"));
MessageBox.Show("Ok");
}
async Task SimulateParse(TaskQueue queue)
{
Random rnd = new Random();
int counter = 0; // representing some piece of data
for(int i = 0; i < 500; i++)
{
dispatch.Invoke(() => lblStatus2.Content = string.Format("Check {0}", ++counter));
Thread.Sleep(25); //no await variant
//await Task.Delay(25);
// if some condition matched - queue work
if (rnd.Next(1, 11) < 2)
{
// Blocks thread even though Enqueue() has await inside
queue.Enqueue(SimulateWork, counter);
//Task.Run(() => queue.Enqueue(SimulateWork, counter));
}
}
}
async Task SimulateWork(object par)
{
dispatch.Invoke(() => lblStatus.Content = string.Format("Working with {0}", par));
Thread.Sleep(400); //no await variant
//await Task.Delay(400);
}
It seems, that it works only if launched task have await inside itself, i.e. if you trying to launch task without await inside it, it will block current thread.
This rig will work as intended, if commented lines are used, but it looks like excessive amount of calls, also, real versions of SimulateParse and SimulateWork does not need to await anything. Main question is - what is the optimal way to launch task with non-async function inside of it? Do i just need to encase them in a Task.Run() like in commented rows?
TaskQueue is used here to run task one by one
It will run them one at a time, yes. SemaphoreSlim does have an implicit queue, but it's not strictly a FIFO-queue. Most synchronization primitives have a mostly-but-not-quite-FIFO implementation, which is Close Enough. This is because they are synchronization primitives, and not queues.
If you want an actual queue (i.e., with guaranteed FIFO order), then you should use a queue, such as TPL Dataflow or System.Threading.Channels.
if you trying to launch task without await inside it, it will block current thread.
All async methods begin executing on the current thread, as described on my blog. async does not mean "run on a different thread". If you want to run a method on a thread pool thread, then wrap that method call in Task.Run. That's a much cleaner solution than sprinkling Task.Delay throughout, and it's more efficient, too (no delays).
I'm trying to use cancellation tokens to break out of a loop from another thread:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace CancellationTokenTest
{
class Program
{
private static CancellationTokenSource token;
static async Task Main(string[] args)
{
token = new CancellationTokenSource();
var t1 = LongProcess1();
for (int i = 0; i < 5; i++)
{
try
{
await Task.Delay(2000, token.Token);
Console.WriteLine($"Main awaited {i}");
}
catch (OperationCanceledException)
{
break;
}
}
Console.WriteLine("Main loop completed");
t1.Wait();
Console.WriteLine("Application end");
}
static async Task LongProcess1()
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(1000);
Console.WriteLine($"LongProcess1 awaited {i}");
}
Console.WriteLine("Submitting cancel");
token.Cancel(); // <------ Blocking execution
Console.WriteLine("Cancellation done!");
}
}
}
The problem is that the line Console.WriteLine("Cancellation done!"); never gets executed, and the Main() method keeps waiting for t1 to complete.
I do not understand why!
I ran this on .NET 5, but it doesn't seem to be framework dependent.
Can someone help me understand what's going on?
This deadlock is almost exactly like the one described on my blog. One thing you need to be aware of is that CancellationTokenSource.Cancel in this case is running the cancellation token continuations on the thread that calls Cancel.
So, here's what's happening:
Main starts LongProcess.
LongProcess uses some awaits and resumes on a thread pool thread.
In the meantime, Main is doing an await Task.Delay.
When LongProcess does the Cancel, it actually resumes executing the Main method on the current thread. You can verify this by placing a breakpoint in your catch block and looking at the call stack.
The current thread (a thread pool thread) then blocks on the task (.Wait()).
Now, LongProcess cannot complete because the thread currently running it is blocked waiting for it to complete.
To fix it, change the Wait() to an await.
The problem is the t1.Wait(); (Wait() is almost always the problem ;p) - this is stealing the thread. Use await t1; instead, and everything will work. Effectively, the cancellation needs to invoke some callbacks (the continuations in the cancellation state) and then return to whatever did the cancellation, and you've inserted a blockage into the cancellation.
This is the code that I wrote to better understand asynchronous methods. I knew that an asynchronous method is not the same as multithreading, but it does not seem so in this particular scenario:
class Program
{
static void Main(string[] args)
{
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
//the line above just makes sure that the console output uses . to represent doubles instead of ,
ExecuteAsync();
Console.ReadLine();
}
private static async Task ParallelAsyncMethod() //this is the method where async parallel execution is taking place
{
List<Task<string>> tasks = new List<Task<string>>();
for (int i = 0; i < 5; i++)
{
tasks.Add(Task.Run(() => DownloadWebsite()));
}
var strings = await Task.WhenAll(tasks);
foreach (var str in strings)
{
Console.WriteLine(str);
}
}
private static string DownloadWebsite() //Imitating a website download
{
Thread.Sleep(1500); //making the thread sleep for 1500 miliseconds before returning
return "Download finished";
}
private static async void ExecuteAsync()
{
var watch = Stopwatch.StartNew();
await ParallelAsyncMethod();
watch.Stop();
Console.WriteLine($"It took the machine {watch.ElapsedMilliseconds} milliseconds" +
$" or {Convert.ToDouble(watch.ElapsedMilliseconds) / 1000} seconds to complete this task");
Console.ReadLine();
}
}
//OUTPUT:
/*
Download finished
Download finished
Download finished
Download finished
Download finished
It took the machine 1537 milliseconds or 1.537 seconds to complete this task
*/
As you can see, the DownloadWebsite method waits for 1.5 seconds and then returns "a". The method called ParallelAsyncMethod adds five of these methods into the "tasks" list and then starts the parallel asynchronous execution. As you can see, I also tracked the amount of time that it takes for the ExecuteAsync method to be executed. The result is always somewhere around 1540 milliseconds. Here is my question: if the DownloadWebsite method required a thread to sleep 5 times for 1500 milliseconds, does it mean that the parallel execution of these methods required 5 different threads? If not, then how come it only took the program 1540 milliseconds to be executed and not ~7500 ms?
I knew that an asynchronous method is not the same as multi-threading
That is correct, an asynchronous method releases the current thread whilst I/O occurs, and schedules a continuation after it's completion.
Async and threads are completely unrelated concepts.
but it does not seem so in this particular scenario
That is because you explicitly run DownloadWebsite on the ThreadPool using Task.Run, which imitates asynchronous code by returning a Task after instructing the provided delegate to run.
Because you are not waiting for each Task to complete before starting the next, multiple threads can be used simultaneously.
Currently each thread is being blocked, as you have used Thread.Sleep in the implementation of DownloadWebsite, meaning you are actually running 5 synchronous methods on the ThreadPool.
In production code your DownloadWebsite method should be written asynchronously, maybe using HttpClient.GetAsync:
private static async Task<string> DownloadWebsiteAsync()
{
//...
await httpClinet.GetAsync(//...
//...
}
In that case, GetAsync returns a Task, and releases the current thread whilst waiting for the HTTP response.
You can still run multiple async methods concurrently, but as the thread is released each time, this may well use less than 5 separate threads and may even use a single thread.
Ensure that you dont use Task.Run with an asynchronous method; this simply adds unnecessary overhead:
var tasks = new List<Task<string>>();
for (int i = 0; i < 5; i++)
{
tasks.Add(DownloadWebsiteAsync()); // No need for Task.Run
}
var strings = await Task.WhenAll(tasks);
As an aside, if you want to imitate an async operation, use Task.Delay instead of Thread.Sleep as the former is non-blocking:
private static async Task<string> DownloadWebsite() //Imitating a website download
{
await Task.Delay(1500); // Release the thread for ~1500ms before continuing
return "Download finished";
}
I'm still getting up to speed with async & multi threading. I'm trying to monitor when the Task I Start is still running (to show in a UI). However it's indicating that it is RanToCompletion earlier than I want, when it hits an await, even when I consider its Status as still Running.
Here is the sample I'm doing. It all seems to be centred around the await's. When it hits an await, it is then marked as RanToCompletion.
I want to keep track of the main Task which starts it all, in a way which indicates to me that it is still running all the way to the end and only RanToCompletion when it is all done, including the repo call and the WhenAll.
How can I change this to get the feedback I want about the tskProdSeeding task status?
My Console application Main method calls this:
Task tskProdSeeding;
tskProdSeeding = Task.Factory.StartNew(SeedingProd, _cts.Token);
Which the runs this:
private async void SeedingProd(object state)
{
var token = (CancellationToken)state;
while (!token.IsCancellationRequested)
{
int totalSeeded = 0;
var codesToSeed = await _myRepository.All().ToListAsync(token);
await Task.WhenAll(Task.Run(async () =>
{
foreach (var code in codesToSeed)
{
if (!token.IsCancellationRequested)
{
try
{
int seedCountByCode = await _myManager.SeedDataFromLive(code);
totalSeeded += seedCountByCode;
}
catch (Exception ex)
{
_logger.InfoFormat(ex.ToString());
}
}
}
}, token));
Thread.Sleep(30000);
}
}
If you use async void the outer task can't tell when the task is finished, you need to use async Task instead.
Second, once you do switch to async Task, Task.Factory.StartNew can't handle functions that return a Task, you need to switch to Task.Run(
tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token);
Once you do both of those changes you will be able to await or do a .Wait() on tskProdSeeding and it will properly wait till all the work is done before continuing.
Please read "Async/Await - Best Practices in Asynchronous Programming" to learn more about not doing async void.
Please read "StartNew is Dangerous" to learn more about why you should not be using StartNew the way you are using it.
P.S. In SeedingProd you should switch it to use await Task.Delay(30000); insetad of Thread.Sleep(30000);, you will then not tie up a thread while it waits. If you do this you likely could drop the
tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token);
and just make it
tskProdSeeding = SeedingProd(_cts.Token);
because the function no-longer has a blocking call inside of it.
I'm not convinced that you need a second thread (Task.Run or StartNew) at all. It looks like the bulk of the work is I/O-bound and if you're doing it asynchronously and using Task.Delay instead of Thread.Sleep, then there is no thread consumed by those operations and your UI shouldn't freeze. The first thing anyone new to async needs to understand is that it's not the same thing as multithreading. The latter is all about consuming more threads, the former is all about consuming fewer. Focus on eliminating the blocking and you shouldn't need a second thread.
As others have noted, SeedingProd needs to return a Task, not void, so you can observe its completion. I believe your method can be reduced to this:
private async Task SeedingProd(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
int totalSeeded = 0;
var codesToSeed = await _myRepository.All().ToListAsync(token);
foreach (var code in codesToSeed)
{
if (token.IsCancellationRequested)
return;
try
{
int seedCountByCode = await _myManager.SeedDataFromLive(code);
totalSeeded += seedCountByCode;
}
catch (Exception ex)
{
_logger.InfoFormat(ex.ToString());
}
}
await Task.Dealy(30000);
}
}
Then simply call the method, without awaiting it, and you'll have your task.
Task mainTask = SeedingProd(token);
When you specify async on a method, it compiles into a state machine with a Task, so SeedingProd does not run synchronously, but acts as a Task even if returns void. So when you call Task.Factory.StartNew(SeedingProd) you start a task that kick off another task - that's why the first one finishes immediately before the second one. All you have to do is add the Task return parameter instead of void:
private async Task SeedingProdAsync(CancellationToken ct)
{
...
}
and call it as simply as this:
Task tskProdSeeding = SeedingProdAsync(_cts.Token);
However, with ASP.NET Web Api, if your request is coming in on one
thread, and you await some function and call ConfigureAwait(false)
that could potentially put you on a different thread when you are
returning the final result of your ApiController function.
Actually, just doing an await can do that. Once your async method hits
an await, the method is blocked but the thread returns to the thread
pool. When the method is ready to continue, any thread is snatched
from the thread pool and used to resume the method.
Source
I've just tested that in a console program:
async Task foo()
{
int y = 0;
while (y<5) y++;
}
async Task testAsync()
{
int i = 0;
while (i < 100)
{
Console.WriteLine("Async 1 before: " + i++);
}
await foo();
while (i < 105)
{
i++;
Console.WriteLine("Async 1 after: " + i);
}
}
Calling await foo() doesn't cause the thread testAsync was running on to return to thread pool, testAsync just runs line by line on the same thread from start to end. What's am I missing here?
What's am I missing here?
You are missing compiler warnings
Warning CS1998 This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
The method foo isn't really asynchronous as there are no await calls in it.
Try adding await Task.Delay in there.