44

Consider Using async without await.

think that maybe you misunderstand what async does. The warning is exactly right: if you mark your method async but don't use await anywhere, then your method won't be asynchronous. If you call it, all the code inside the method will execute synchronously.

I want to write a method that should run async but don't need use await. For example when use a thread

public async Task PushCallAsync(CallNotificationInfo callNotificationInfo)
{
    Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId,
}

I want call PushCallAsync and run async and don't want to use await.

Can I use async without await in C#?

Kissaki
  • 8,810
  • 5
  • 40
  • 42
  • @J.Steen so if use this run async? –  Jul 23 '13 at 09:15
  • 2
    Well. It's all up to what your design issue is, here. What do you expect "async" to do? – J. Steen Jul 23 '13 at 09:15
  • Without `await` your method will execute `Synchronously` – Sriram Sakthivel Jul 23 '13 at 09:16
  • @J.Steen i want run in other thread –  Jul 23 '13 at 09:17
  • 6
    If you're not going to await this method why bother declaring it async at all? The text that you quote sums it up pretty nicely IMO. – BoltClock Jul 23 '13 at 09:17
  • Run in other thread. Sure. Do you want to wait for it to finish? Or just fire-and-forget? From the content, looks like just a fire-and-forget. – J. Steen Jul 23 '13 at 09:18
  • 1
    I think you just use [`Task.Run`](http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.run.aspx) directly. `async`/`await` isn't really a multithreading mechanism, in fact I think the runtime executes things on as few threads as possible. It's mostly about the compiler automatically transform your code into continuation-passing style so you only *wait for results* (or completion) of a background operation when absolutely necessary. When you don't need to wait (or `await`) for the results of your call, this is not the language feature you're looking for. – millimoose Jul 23 '13 at 09:28
  • See also what the docs have to say about this: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx#BKMK_Threads (I was wrong, there's just a *single* thread and nonblocking IO involved. – millimoose Jul 23 '13 at 09:38
  • 4
    @millimoose: The way `async` interacts with threads is a bit more complex, and the default behavior can be easily overridden. `async` is not a multithreading mechanism, nor does it always run on a single thread. [I have a blog post that summarizes how `async` schedules its continuations](http://blog.stephencleary.com/2012/02/async-and-await.html). – Stephen Cleary Jul 23 '13 at 15:41
  • @StephenCleary So it'd be fairer to say that (as far as the OP needs to be concerned) an `async` method achieves asynchronicity using some mechanism appropriate to the use case, and that this mechanism can be considered an implementation detail? And that from this follows that merely declaring a method as `async` does not in fact introduce asynchronicity, and the keyword serves only to trigger the compiler rewrites from sequential style to CPS. – millimoose Jul 23 '13 at 18:53
  • 2
    @millimoose: Not an implementation detail. It is clearly specified - and has to be, so its behavior is always predictable and reliable (once you understand the mechanism). – Stephen Cleary Jul 23 '13 at 18:58

5 Answers5

41

You still are misunderstanding async. The async keyword does not mean "run on another thread".

To push some code onto another thread, you need to do it explicitly, e.g., Task.Run:

await Task.Run(() => Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId));

I have an async/await intro post that you may find helpful.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • is Task.Run can be not awaited? – toha Jul 14 '22 at 17:46
  • @toha: I don't understand your question. The `Task.Run` is `await`ed. – Stephen Cleary Jul 15 '22 at 00:45
  • I use task, and not await. but sometimes it is not called until completed.. when out of the function block.. I hope the task running until complete even main process out of function block. can it be done? If I use await it is takes long time so I need It is not awaited and called until finished even out of function call – toha Jul 15 '22 at 03:55
  • @toha: It's not really clear what you're asking. Can you ask your own question? – Stephen Cleary Jul 15 '22 at 14:55
  • No the question is same.. I am not await my async task. but I use it on Exception Block and the async Task function is not completely finish when awaiting another function call I think this is about async task life cycle when fired not await and on exception block.. do you have article about that Sir? – toha Jul 15 '22 at 15:28
  • @toha: Please see [this series](https://blog.stephencleary.com/2021/01/asynchronous-messaging-1-basic-distributed-architecture.html). – Stephen Cleary Jul 15 '22 at 15:53
  • excellent! I will read that thank You. Types of async , Durable, non durable, etc I will learn this – toha Jul 16 '22 at 01:28
35

If your Logger.LogInfo is already async this is enough:

public void PushCallAsync(CallNotificationInfo callNotificationInfo)
{
    Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId,
}

If it is not just start it async without waiting for it

public void PushCallAsync(CallNotificationInfo callNotificationInfo)
{
    Task.Run(() => Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId));
}
Moerwald
  • 10,448
  • 9
  • 43
  • 83
Visions
  • 919
  • 1
  • 7
  • 17
  • I read it, seems to me more like a style difference than an actual difference in bahavior, isn't it? – Visions Jul 24 '13 at 07:10
  • 10
    No. `Task.Run` uses different defaults: `DenyChildAttach` and `TaskScheduler.Default`. The `TaskScheduler` is particularly important because `StartNew` will use `TaskScheduler.Current` by default, which makes it schedule the delegate differently based on the context of the caller. This has tripped up so many people that many teams have adopted code rules that never allow `StartNew` unless the `TaskScheduler` is specified. – Stephen Cleary Jul 24 '13 at 11:03
  • Didn't know that, thx for the info. Changed the code to Task.Run – Visions Jul 25 '13 at 07:47
  • 7
    By convention you should not name a method "Async" unless it has the "async" keyword. – Graeme Wicksted Nov 08 '16 at 14:36
  • If your Logger.LogInfo is already async this is enough: >> My method is already async but when I debug it, the code exit before completion to finish.. how to make sure LogInfo is always exit until finish and with no await ? – toha Jul 14 '22 at 17:18
6

You're misunderstanding async. It actually just tells the Compiler to propagate the inversion of control flow it does in the background for you. So that the whole method stack is marked as async.

What you actually want to do depends on your problem. (Let's consider your call Logger.LogInfo(..) is an async method as it does eventually call File.WriteAsync() or so.

  • If you calling function is a void event handler, you're good. The async call will happen to some degree (namely File.WriteAsync) in the background. You do not expect any result in your control flow. That is fire and forget.
  • If however you want to do anything with the result or if you want to continue only then, when Logger.LogInfo(..) is done, you have to do precautions. This is the case when your method is somehow in the middle of the call-stack. Then Logger.LogInfo(..) will usually return a Task and that you can wait on. But beware of calling task.Wait() because it will dead lock your GUI-Thread. Instead use await or return the Task (then you can omit async):

public void PushCallAsync(CallNotificationInfo callNotificationInfo) 
{
   return Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId); 
}

or

 public async void PushCallAsync(CallNotificationInfo callNotificationInfo) 
 {
    await Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId); 
 }
Orkhan Alikhanov
  • 9,122
  • 3
  • 39
  • 60
Robetto
  • 739
  • 7
  • 20
  • 7
    You should not use `async void` unless it is an event handler. That is the only acceptable time and is the only correct way of making an async event handler. If your synchronous method is `void`, it should be `async Task`. This is due to exception handling where event handlers will throw on the `SynchronizationContext` (similarly to synchronous events) rather than the caller. If possible the async event handler should be a thin private wrapper an `async Task` function (so it can be tested or re-used). – Graeme Wicksted Nov 08 '16 at 14:45
  • @GraemeWicksted you are certainly right and I support 'do not do async void' but for the understanding it is what I need - and eventually you'll usually have async void somewhere – Robetto Aug 15 '19 at 09:09
  • 1
    I'm not sure I understand your reply. How is it justified here? Looks like it should be `async Task` to me as it is not an event handler. Remember: `async Task` is essentially `async Task` but the latter simply does not exist. The only difference between `async void` and `async Task` is exception propagation and it only makes sense on event handlers. I have not observed a concrete example that would indicate otherwise. – Graeme Wicksted Aug 16 '19 at 14:46
2

If Logger.LogInfo is a synchronous method, the whole call will be synchronous anyway. If all you want to do is to execute the code in a separate thread, async is not the tool for the job. Try with thread pool instead:

ThreadPool.QueueUserWorkItem( foo => PushCallAsync(callNotificationInfo) );
ya23
  • 14,226
  • 9
  • 46
  • 43
  • 1
    Favour the Parallel Task library, rather than calling the threadpool directly... this can have rather unforeseen consequences depending on the platform (WinRT, Win, WP8, etc). In my experience. =) – J. Steen Jul 23 '13 at 09:23
  • 2
    You got me interested... Could you please provide more details on what the consequences could be? – ya23 Jul 23 '13 at 09:27
  • Well. I've managed to completely choke the CPU on several devices, using `QueueUserWorkItem` (using only two threads, mind you, so it's touchy as well), whereas the Task library will manage it perfectly. I've also had an issue where I've queued several threads, and none reaches the callback until they're all done, in a sort of thread-avalanche. – J. Steen Jul 23 '13 at 09:30
  • 1
    Consider [this question and its answers, and further reading](http://stackoverflow.com/questions/9200573/threadpool-queueuserworkitem-vs-task-factory-startnew). – J. Steen Jul 23 '13 at 09:38
  • 2
    *If Logger.LogInfo is not a synchronous method* don't you mnean If *Logger.LogInfo **is** a synchronous method*?? – Liam Jul 08 '16 at 14:26
0

Given your example

public async Task PushCallAsync(CallNotificationInfo callNotificationInfo)
{
    Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId,
}
  • Logger.LogInfo is called synchronously
  • The async keyword gives the method PushCallAsync the capability to await, but it never awaits anything

If your intention is to make the method run asynchronously - as the name PushCallAsync fittingly implies too -, you have find an alternative to synchronously calling LogInfo.

If a LogInfoAsync method exists, trying to evade using await is ill-advised. Await is important because:

  • It captures and throws exceptions that may occur on the task execution - which would otherwise be lost / unhandled
  • It ensures execution order by waiting for the result

If you specifically want a fire-and-forget behavior, where you do not depend on execution order (e.g. in this case don't care about order of the log messages), you call LogInfoAsync() without awaiting the result.

Since you don't use any await you do not mark the method async. What makes it asynchronous is not the async keyword but it calling other methods asynchronously.

public Task PushCallAsync(CallNotificationInfo callNotificationInfo)
{
    // Fire and forget - we do not care about the result, failure, or order of this log message
    _ = Logger.LogInfoAsync("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId,
    Task.CompletedTask;
}

or non-async

public void PushCall(CallNotificationInfo callNotificationInfo)
{
    // Fire and forget - we do not care about the result, failure, or order of this log message
    _ = Logger.LogInfoAsync("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId,
}

Not that the method name Push implies it is in order. So I would name it differently if you really don't care about order. Otherwise, as Push implies, using await would be correct to ensure order.

Kissaki
  • 8,810
  • 5
  • 40
  • 42