12

Given an asynchronous method that does both CPU and IO work such as:

Public Async Function RunAsync() As Task
    DoWork()
    Await networkStream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(False)
End Function

Which of the following options is the best way to call that asynchronous method from Task.Run in Visual Basic and why?

Which is the VB equivalent for C# Task.Run(() => RunAsync())?

Await Task.Run(Function() RunAsync())
' or
Await Task.Run(Sub() RunAsync())

Are the Async/Await keywords within Task.Run necessary or redundant? This comment claims they're redundant, but this answer suggests it might be necessary in certain cases:

Await Task.Run(Async Function()
                   Await RunAsync()
               End Function)

Is ConfigureAwait useful within Task.Run?

Await Task.Run(Function() RunAsync().ConfigureAwait(False))

Await Task.Run(Async Function()
                   Await RunAsync().ConfigureAwait(False)
               End Function)

Which of the above 5 Task.Run options is best practice?

Note: There's a similar question How to call Async Method within Task.Run? but it's for C#, the selected answer has negative votes, and doesn't address ConfigureAwait.

Community
  • 1
  • 1
Mike Henry
  • 2,401
  • 1
  • 25
  • 34
  • 3
    Lets ask another question: why would you want to wrap an already async method in a call to `Task.Run` instead of just doing `Await SendAsync()?` – Peter Bons Feb 02 '17 at 18:42
  • This is just a simple example, real usage would be more complex with IO and CPU, therefore being desirable to run it on the threadpool. – Mike Henry Feb 02 '17 at 18:47
  • Bu even then, the underlying async method will already run on its own thread or wait for IO to complete. So you could possible create another thread on top of the original one. My point is, `Task.Run` intended to run non-async methods . – Peter Bons Feb 02 '17 at 18:51
  • I see your point for most IO cases. But might it run the CPU work on the main thread which would be undesirable? [This answer](http://stackoverflow.com/a/18015586/14934) gives some explanation as to when it might be desirable to use Task.Run. – Mike Henry Feb 02 '17 at 18:55
  • 1
    This is an important topic for people to understand. I needed this solution because I was provided with a library for sending emails that only exposes async functions, and I did not want to convert all calling subs that need to send emails to an async functions. – Hawkeye Aug 15 '17 at 19:32
  • 1
    Worth noting: I've recently discovered that C# lambdas don't make an easily visible distinction between `Func`s and `Action`s. The way to distinguish one from the other is to determine whether the code in the lambda's method body returns a result. If so, it's a `Func`. If not, it's an `Action`. – InteXX Jan 27 '18 at 03:54

1 Answers1

26

Which is the VB equivalent for C# Task.Run(() => RunAsync())?

My VB is horribly rusty, but it should not be the Sub one, so I'd go with:

Task.Run(Function() RunAsync())

Are the Async/Await keywords within Task.Run necessary or redundant?

I have a blog post on the subject. In this case, they're redundant because the delegate is trivial.

Is ConfigureAwait useful within Task.Run?

Only if you do an Await.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Thanks for the info and the link to the helpful blog post. From what I understand, it sounds like in this trivial case `Task.Run(Function() RunAsync())` is the best option since it's just passing through, and uncaught exceptions will still be captured in an AggregateException when awaiting Task.Run. And based on the blog post, it sounds like the Await/Async keywords and therefore ConfigureAwait may be useful in more complex calls from Task.Run. – Mike Henry Feb 02 '17 at 22:57
  • Function vs Sub for the lambda does sounds better, since then the lambda can return a Task instead of returning void. – Mike Henry Feb 02 '17 at 23:00
  • 1
    @MikeHenry: Yes, except for the `AggregateException`. `Task.Run` doesn't wrap into an `AggregateException`; as long as you use `Await` and not `Wait`/`Result`, you shouldn't have to mess with aggregate exceptions. – Stephen Cleary Feb 02 '17 at 23:04
  • Ah, that makes sense. Thanks for the clarification. – Mike Henry Feb 02 '17 at 23:06