I have a Task that should run asynchronously but runs synchronously.
I've created a Task:
var task = Task<int>.Factory.FromAsync(proxy.BeginSaveImage(sp, new AsyncCallback(CompleteSave), state), proxy.EndSaveImage);
int res = task.Result;
The Task calls an asynchronous WCF service. The WCF Service function:
public IAsyncResult BeginSaveImage(statePackage sp, AsyncCallback callback, object state)
{
gStatePackage = sp;
// Create a task to do the work
var task = Task<int>.Factory.StartNew(this.SaveImage, state);
return task.ContinueWith(res => callback(task));
}
I run the task inside a loop.
My problem is that when I run it, it doesn't run in parallel and each call for task.Results waits for the task to be complete before continuing on.
When I put the Task code in a function SaveImageProcedure and call it from within the loop like that:
Task.Factory.StartNew(() =>
{
SaveImageProcedure(sp);
});
It runs asynchronously. I don't want to wrap an async call with another async wrapper. Why the call using task.Result doesn't run asynchronously and how can I change it to run async without wrapping it like I did to run async (or if it runs async, don't wait for results and continue the operation)?
I don't need the task returned value, and don't mind getting it but with the code continue to run for the next iteration in the loop without waiting for the result to continue.
By reading the Result field, you are waiting for the result to be available.
Try evaluating the result in a Continue or, use await
I'd expect something that looks like (pseudo code):
var task = Task<int>...
task.ContinueWith(result => {
if (result != expected) throw new Exception("...");
});
Essentially, if you are doing one task and you have to get the result, you will be doing this part of the work flow synchronously. Task and async only allow you to do other things while you wait, they don't do any magic.
I recommend you read my async intro and MSDN article on best practices. In particular, use await instead of ContinueWith or Result.
On the server side, there's no point in wrapping the work inside StartNew. It should just execute synchronously if it has only synchronous work to do.
Related
I got this code:
static async Task AsynchronousProcessing()
{
Task<string> t1 = GetInfoAsync("Task 1", 3);
Task<string> t2 = GetInfoAsync("Task 2", 5);
string[] results = await Task.WhenAll(t1, t2);
foreach (string result in results)
{
WriteLine(result);
}
}
static async Task<string> GetInfoAsync(string name, int seconds)
{
await Task.Delay(TimeSpan.FromSeconds(seconds));
return "hi";
}
When reaching the line Task<string> t1 = GetInfoAsync("Task 1", 3);, will it already start asynchronously with the await Task.Delay(TimeSpan.FromSeconds(seconds)); and move on to the rest of the method, or will it return immediatly a task without actually starting it?
And if it won't start when calling the method, will the tasks start only when calling the Task.WhenAll(t1,t2)? Since if it doesn't start right away, I would expect to see GetInfoAsync doing something like return new Task<string>(() => ...);
I recommend reading my async intro.
In summary, all async methods begin executing synchronously. So when AsynchronousProcessing calls GetInfoAsync, then GetInfoAsync begins running, synchronously, just like any other method. An async method can become asynchronous when there is an await. In this case, GetInfoAsync calls Task.Delay (again, synchronously, just like a regular method call), and then passes its Task to await. At this point, await will examine the task; if it is already complete, then it will continue running synchronously; otherwise, it will act asynchronously and return an incomplete task from GetInfoAsync.
The tasks returned from async methods are in progress. I don't usually use the term "running", because they are not actually running code on a CPU and because the task status is not actually Running. This "asynchronous in-progress" state is unfortunately named WaitingForActivation (even though it's not waiting for anything).
Since if it doesn't start right away, I would expect to see GetInfoAsync doing something like return new Task(() => ...);
The async keyword handles the creation of the task. As noted above, the tasks are "hot" or "in progress". This is not the same as "running" or "started", both of which imply tasks that run CPU code. There are two types of tasks: what I call Delegate Tasks and Promise Tasks. The ones returned from async methods are Promise Tasks.
If there aren't any new threads with await, then how will they run in parallel?
They run concurrently (not in parallel, since there is no additional thread blocked on the delay).
However, when the await Task.Delay(..) completes, then there could possibly be another thread used. The await will resume on its captured context and execute the return "hi"; in that context. If the captured context is a thread pool context, then that one line of code will be executed on a thread pool thread. The async state machine translates the return into code that completes the task that was previously returned from that async method.
GetInfoAsync is called and runs synchronously just as usual until it hits the await Task.Delay(TimeSpan.FromSeconds(seconds)) line. It then returns an uncompleted task back to the calling AsynchronousProcessing() method.
So, yes, the task is indeed being started when you call the method, i.e. you don't have to await GetInfoAsync for the Task.Delay method to get called.
My problem is that when a Task has a Task.WhenAll() call (running other Tasks) the line of WhenAll() makes the consuming code continue execution, unlike what I would expect. So the following code outputs "finished" immediately when the Task.WhenAll() is hit, not after all the tasks in its argument are finished.
// Just a simple async method
public Task DoWorkAsync()
{
return Task.Factory.StartNew(
() =>
{
// Working
});
}
// This one used the previous one with Task.WhenAll()
public Task DoLoadsOfWorkAsync()
{
return Task.Factory.StartNew(
async () =>
{
// Working
// This line makes the task return immediately
await Task.WhenAll(DoWorkAsync(), DoWorkAsync());
// Working
});
}
// Consuming code
await DoLoadsOfWorkAsync();
Console.WriteLine("finished");
I'd expect the WriteLine() to be called when the last line of DoLoadsOfWorkAsync() is executed.
What am I doing wrong? Thanks in advance.
Task.WhenAll returns a new Task immediately, it does not block. The returned task will complete when all tasks passed to WhenAll have completed.
It is an asynchronous equivalent to Task.WaitAll, and this is the method to use if you want to block.
However you have another problem. Using Task.Factory.StartNew and passing an async delegate seems to lead to a type of Task<Task> where the outer task completes when the inner task starts to execute (rather than when it has completed).
Using the newer Task.Run avoids this.
I'm a C# newbie, so I'm struggling to understand some concepts, and I run into a piece of code that I'm not quite understanding:
static void Main(string[] args)
{
Task.Run(async () => { await SomeClass.Initiate(new Configuration()); }).Wait();
while (true) ;
}
As I understand, this runs a task which initiates a method. This method runs, and then, once it finished, it gets into an infinite loop waiting. It feels that either the code doesn't make sense, or that I'm not understanding right.
Thanks
You can break this apart into several parts:
async () => { await SomeClass.Initiate(new Configuration()); }
Is a lambda expression that defines an async method that just awaits another method. This lambda is then passed to Task.Run:
Task.Run(async () => { await SomeClass.Initiate(new Configuration()); })
Task.Run executes its code on a thread pool thread. So, that async lambda will be run on a thread pool thread. Task.Run returns a Task which represents the execution of the async lambda. After calling Task.Run, the code calls Task.Wait:
Task.Run(async () => { await SomeClass.Initiate(new Configuration()); }).Wait();
This will block the main console app until the async lambda is completely finished.
If you want to see how it's broken out further, the following is roughly equivalent:
static async Task AnonymousMethodAsync()
{
await SomeClass.Initiate(new Configuration());
}
static void Main(string[] args)
{
var task = Task.Run(() => AnonymousMethodAsync());
task.Wait();
while (true) ;
}
I wrote a little .NET Fiddle that simply added some Console.WriteLine calls to hopefully help show you the order of execution.
The Task.Run returns a Task that represents an asynchronous operation, (a method that runs asynchronously). It's parameter in this situation is the Func<Task>. Your code has utilized the async and await keywords, as such your call to SomeClass.Initiate is an async lambda.
Queues the specified work to run on the thread pool and returns a proxy for the task returned by function.
All async code should return an awaitable, unless in the rare situation where you're authoring an event handler that needs to start some async calls.
The Task that is returned from the invocation of Task.Run is immediately invoking .Wait().
Waits for the Task to complete execution within a specified time interval.
That call to Task.Run is not needed, you could simply invoke .Wait() on the Task that is returned from the SomeClass.Initiate method.
As for the infinite loop, a console application exits immediately -- you could simply do the Console.ReadLine and wait for the user to hit the Enter key for example.
Most likely SomeClass.Initiate is synchronous method marked async for no reason. Than someone tried to make it really run asynchronously and added Task.Run to run it on separate thread. But since it is console application - added .Wait() to wait for the operation to finish.
And while (true) ; is there because when running in VS console app closes without trace when it is done, more obvious way - add Console.ReadLine() call. while(true) is not realated to async portion at all as code will not leave .Wait() call before operation is done.
Another possibility is author was experimenting with async and did not have .Wait() or .Result - thus while(true) would give chance for operation to finish.
See async at console app in C#? for proper way to run async code in console app (just .Wait() or .Result)
The typical way of writing an async Task method is as follows:
public async Task<int> LongCalculationAsync(int arg)
{
int res = doSomeLongCalculation();
res += await CallSomeOtherTaskAsync();
res ++;
return res;
}
When written like this, the first part (before the await) is performed synchronously, and then another Task is created and started on perhaps another thread, which is then continued by a task that contains the last 2 lines and is run on the original context.
The problem is that the synchronous part is performed on whichever scheduler the caller runs on. But my problem is that I know that I want the task returned to run using a specific scheduler, even if the method is called from the UI thread.
Is there a way for the async method itself to decide on the context?
Use Task.Factory.StartNew to push work to any scheduler you want. For the default thread-pool use Task.Run. It's quite easy:
await Task.Factory.StartNew(() => doSomeLongCalculation(), ...)
I have an asynchronous method that does something peculiar (at least, looks peculiar to me):
public async ReturnType MethodNameHere()
{
var result = await DoSomethingAsync(); // this could take a while (dozens of seconds)!
// no other processing after awaiting the result of the asynchronous task
return result;
}
This method is consumed by another method, as follows (meant to run in an infinite loop):
private async void ProcessSomething()
{
// some console printing
var returnType = await MethodNameHere();
// do some _actual_ work here, process stuff, etc
ProcessSomething(); // call the same method again
}
Now this task is running via Task.Factory.StartNew(ProcessSomething, TaskCreationOptions.LongRunning), which, along with other tasks that do similar work, are combined with Task.Factory.ContinueWhenAll.
Question
Is it correct to assume that, if the scheduler ends up putting each task on the same thread as one another, the tasks will not block each other?
If that is the case, is there even any benefit to calling the asynchronous version of DoSomething if it is to be running "alone" in a Task?
If you want to do some work before the async operation finishes, just do that work before awaiting the returned Task.
However, the primary purpose of async operations is to avoid consuming threads while waiting for non-blocking operations.