2

First of all I searched here and have seen many similar questions but none of them is what I want.

I've a function that takes some time to return a value To simplify, let's say it's:

Private Function longProcess() As Boolean
    Threading.Thread.Sleep(10000)
    Return True
End Function

I want to run it and get its value, let's say on clicking Button1

I tried the following code and it's working perfectly

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    'some stuff
    Dim output As Boolean = Await Task.Run(Of Boolean)(Function() longProcess())
    'continue another stuff when the longProcess completes
End Sub

If this way is good enough ? if not, What problems it may have ?

I was using another way but it was making CPU usage higher because of Application.DoEvents()

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    'some stuff
    Dim awaiter = Task.Run(Of Boolean)(Function() longProcess()).GetAwaiter()
    Do While Not awaiter.IsCompleted
        Application.DoEvents()
    Loop
    Dim output As Boolean = awaiter.GetResult()
    'continue another stuff when the longProcess completes
End Sub
Youssef13
  • 3,836
  • 3
  • 24
  • 41
  • 1
    Do you have problems when you use first option? If not continue using it until you face some issues. – Fabio Aug 26 '17 at 15:54
  • Not facing problems with code doesn't mean it's okay. I'm asking if it's technically good or there is a better short way to do so. – Youssef13 Aug 26 '17 at 15:56
  • What you expect to be "shorter" then one line of code. With your first option you execute function on another thread, which correctly awaited in UI client event handler. So if first option doesn't violate your requirements you can continue using it. In case when long running function using some external resources (fily system, database, web services etc) - then first option is not best practice, because external resources can be accessed more effectively with `async-await` without using extra threads – Fabio Aug 26 '17 at 16:01
  • In my case, The **longProcess()** function is sending HttpWebRequest. – Youssef13 Aug 26 '17 at 16:10

2 Answers2

4

Based on the comments, that your "long running" function sending http request, you should use "fully" asynchronous approach without extra threads provided by Task.Run

Private Async Function SendRequest() As Task(Of Boolean)
    Using (client As New HttpClient())
        client.BaseAddress = new Uri("http://your:api/")

        Dim response As HttpResponseMessage = Await client.GetAsync(pathToResource)

        Return response.IsSuccessStatusCode
    End Using
End Function

Then in button click

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    'some stuff
    Dim output As Boolean = Await SendRequest()
    'continue another stuff when the longProcess completes
End Sub

Above approach is better then your, which used Task.Run, because when you send request from another thread, that thread do nothing, but just waiting for response. Where by using asynchronous async-await methods of HttpClient(or some other class) whole work will be done on one thread without blocking main UI thread of your application.

Notice about HttpClient: even in the sample above instance of HttpClient used in Using block you should use only one instance for your application lifetime.

From Microsoft docs:

HttpClient is intended to be instantiated once and re-used throughout the life of an application. Especially in server applications, creating a new HttpClient instance for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors.

Fabio
  • 31,528
  • 4
  • 33
  • 72
  • Thanks for your answer. I'm using HttpWebRequest, not HttpClient. I really don't know how to edit your code sample to work with HttpWebRequest. I appreciate if you edited it. I also hope you tell more about why this way is better than mine and what do you mean by "fully" asynchronous. – Youssef13 Aug 26 '17 at 16:28
  • 1
    @Youssef13, you can find this answer useful, there are more links provided about HttpClient - [https://stackoverflow.com/a/27737601/1565525](https://stackoverflow.com/a/27737601/1565525) – Fabio Aug 26 '17 at 16:46
  • Thanks for reply :) I could do it with HttpWebRequest, There is GetResponseAsync() method But I'm still wondering why this is better than using Task.Run – Youssef13 Aug 26 '17 at 17:31
  • 1
    Because with `Task.Run` you will use another thread, where with `...Async` method all job will be done in one thread. With `Task.Run` you will reserve a thread which will do nothing - only waiting for response. This is main benefit of `async-await` feature – Fabio Aug 26 '17 at 17:32
  • Does doing the job in two threads uses system resources ? Does it use more RAM for example ? Does it lead to some lag on the app after much time of usage ? – Youssef13 Aug 26 '17 at 17:35
  • 1
    Threads are not unlimited, so they can be considered as System resource too. – Fabio Aug 26 '17 at 17:36
3

I would say it is a standard practice. You could also make the long function async as well

Private Async Function longProcessAsync() As Task(Of Boolean)
    Await Task.Delay(10000) ' Simulating web service call
    Return True
End Function

And await it in the event handler

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    'some stuff
    Dim output As Boolean = Await longProcessAsync()
    'continue another stuff when the longProcess completes
End Sub

Reference: Async/Await - Best Practices in Asynchronous Programming

Async All the Way

...“Async all the way” means that you shouldn’t mix synchronous and asynchronous code without carefully considering the consequences.

...

Allowing async to grow through the codebase is the best solution, but this means there’s a lot of initial work for an application to see real benefit from async code. There are a few techniques for incrementally converting a large codebase to async code, but they’re outside the scope of this article. In some cases, using Task.Wait or Task.Result can help with a partial conversion, but you need to be aware of the deadlock problem as well as the error-handling problem.

Nkosi
  • 235,767
  • 35
  • 427
  • 472