I need to run, lets say, 20 parallel tasks. The task function that is being called is a long running function and can generate errors. I wanted to detect that from the caller and revive the task.
While I understand that handling the exception "inside the task" and rerunning the task is a solution, I was also thinking if there are means to detect this from the caller itself and run the failing task again. When I revive the task it should fall under the bucket of 20 tasks running so that the calling process continues to wait for all the tasks running including the new revived task via Await Task.WhenAll(tasks)
Here is the code on the caller side that creates 20 different task and waits for all of them to finish.
Try
watch = New Stopwatch
watch.Start()
cs = New CancellationTokenSource
Timer1.Enabled = True
Dim tasks = Enumerable.Range(1, 20).Select(Function(i) Task.Run(Function() GetAndProcessMessagesAsync(cs.Token)))
Await Task.WhenAll(tasks)
Catch ex As Exception
MsgBox(ex.Message)
Timer1.Enabled = False
End Try
PS: I have checked the below links for a solution but did not find something suitable that I could try. But definitely would think another method could be to use the TaskContinuationOptions option but am really not sure how and where that would fit in my code above.
Task Parallel Library - How do you get a Continuation with TaskContinuationOptions.OnlyOnCanceled to Fire?
catching parallel tasks if they stop prematurely
I think that doing it inside the Task is the best option, but that doesn't mean you have to change GetAndProcessMessagesAsync. The code could look like this:
Async Function RetryAsync(func As Func(Of Task)) As Task
While True
Try
Await func()
Catch
' TODO: don't catch all exceptions all the time
End Try
End While
End Function
…
Dim tasks = Enumerable.Range(1, 20).Select(
Function(i) Task.Run(
Function() RetryAsync(
Function() GetAndProcessMessagesAsync(cs.Token))))
Also, if GetAndProcessMessagesAsync is truly asynchronous, you might not need that Task.Run.
Related
I'm new to VB and not very familiar with async functions.
I need to fix a bug in existing code where some code is building a report before the data has finished loading.
The problem is that BuildReport() is getting called before the loadOption1(data) has been called. I need the application to wait for all of LoadAsync() to complete before running, but when it waits for GetData(), the application is returning to start() and running BuildReport() too early.
The code looks roughly like this:
Public Async Sub start()
await LoadAsync()
BuildReport() ' this must not run until everything in Load is complete
End Sub
Public Async Function LoadAsync() As System.Threading.Tasks.Task
'this is called from other locations, not just from Start()
dim data = await GetData() 'call to database
' at this point start() continues to run
' but we need it to keep waiting for these next calls to complete
'these calls are synchronous, builds objects needed for the report
loadOption1(data)
loadOption2(data)
loadOption3(data)
'now we want to return to start()
End Function
Public Async Function GetData(s As Scenario) As Task(Of DataResults)
...
Dim resp = Await Task.Run(Function() ConfigWebService.FetchIncentives(req)) ' soap call
End Function
(each function is in separate classes within the same project)
I have tried removing the await from the start function; the options load after BuildReport() is called.
if I remove the await from the GetData() call, passing data.result to the loadoptions functions, the whole application just hangs forever.
I'm stumped. Any tips would be greatly appreciated!
edit: updated the sample to correctly reflect the actual code
Update:
I have tried everything I can think of, from .ContinueWith(False), to attaching to the parent task, to using .ContinueWith(), but nothing has worked so far.
As soon as the code hits the await inside the LoadAsync(), the task in Start() is considered complete
the only thing that seemed to work was to making a isLoading flag that gets set to true at the start of LoadAsync(), then set to false after the LoadOptions3() finished. Then checking if the flag's value has changed in a loop with a delay
Public Async Sub start()
await LoadAsync()
For i As Integer = 1 To 50
If Not IsLoading Then Exit For
Await System.Threading.Tasks.Task.Delay(100)
Next
BuildReport()
End Sub
I'm trying to setup a socket listener in a Universal Windows Platform App. The app will run on a Raspberry PI and listen for communications from an in-motion scale.
I've setup a StreamSocketListener in my mainVM file. I've got commands to start and stop it. I've followed the few guides online i can find, but it won't fire the event when the scale sends the data. I've tested with a listener app that I downloaded, so i can confirm that the messages are being sent and my computer is able to receive them (no firewall issues). (I also know that only one app can listen to a port, so this tester is off when testing my program.)
I'll get one hit on the handler function when it first starts the connection, but i never get anything again. No errors, just nothing happening.
Commands:
Public Property cmdStart As New Command(Async Sub() Await StartListener(), True)
Public Property cmdStop As New Command(Sub() StopListener(), True)
Command Subroutines:
Private Async Function StopListener() As Task
If Connected Then
Await _prePriceListener.CancelIOAsync()
RemoveHandler _prePriceListener.ConnectionReceived, Nothing
_prePriceListener.Dispose()
Connected = False
End If
End Function
Private Async Function StartListener() As Task
If ValidateInput() Then
Try
_prePriceListener = New StreamSocketListener()
_prePriceListener.Control.KeepAlive = False
AddHandler _prePriceListener.ConnectionReceived, AddressOf HandlerReceived
Await _prePriceListener.BindServiceNameAsync("6021")
Connected = True
Catch ex As Exception
Message = ex.Message
End Try
End If
End Function
Event Handler:
Private Async Function HandlerReceived(sender As StreamSocketListener, args As StreamSocketListenerConnectionReceivedEventArgs) As Task
Dim msgReceived As String = ""
Dim inStream As Stream
Dim reader As StreamReader
Try
inStream = args.Socket.InputStream.AsStreamForRead()
reader = New StreamReader(inStream)
Catch ex As Exception
Return
End Try
Try
If reader IsNot Nothing Then
msgReceived = Await reader.ReadLineAsync()
HandlePrepriceRead(msgReceived)
End If
Catch ex As Exception
Return
End Try
End Function
I've tried with _prePriceListener.Control.KeepAlive set to both true and false. When true, it hangs on msgReceived = Await reader.ReadLineAsync() in the handler. I suspect that is the issue, but every fix i've found made no difference. My only other suspicion is incorrect use of Async/Await but i can't find much information to either confirm or deny that. (I think i know what i'm doing with that, but i may be at that level of knowing just enough to be dangerous...)
Anyway, any input would be greatly appreciated. Thanks.
Are you sure that the scale is sending an EOL (\n or \r or \r\n) HEX 13 and or 10 ? I could see your code waiting for an something the scale is never sending. *FYI I'm not sure what kind of scale you are using or how quickly it talks or your application but if the scale is on continuous feed this probably isn't going to work very well for you as the scale and all the parts along the way will buffer things and the weight will be gone by the time you process this event (after a while atleast)
I've read a lot of tutorials with regards to await/async and i've understood that when it comes to await keyword it will go back to the main context then go back to await (when it's finished) and then continue further if there's something after away within async method. I also read that there is something like ConfigureAwait(False) which simply means that with opposite to i've written before when it comes to await it will not go back to main context at this point but will stay here and wll wait on await to be finished and then continue in async method after that will go back to main cotext. So diffrence is it will not go back to main context but wait on await. Correct if i am wrong. Based on that knowledge in first example i should see following result:
After DoStafAsync
Done running the long operation asynchronously.
but when i just simply add ConfigureAwait(False) it should be:
Done running the long operation asynchronously.
After DoStafAsync
as it will not go back to context but will wait and after async method finishes then will go back. Somehow i see the result as in first output. What i am missing?
Public Class Form1
Sub LongOperation()
Threading.Thread.Sleep(5000)
End Sub
Private Async Function DoStafAsync() As Task
lblStatus.Text = "Running a long operation asynchronously... (UI thread should be fully responsive)"
Await Task.Run(Sub()
LongOperation()
End Sub).ConfigureAwait(False)
lblStatus.Text = "Done running the long operation asynchronously."
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
dfg
lblStatus.Text = "After DoStafAsync"
End Sub
Async Sub Dfg
Await DoStafAsync
End Sub
End Class
Additional question: Some people claims that tasks are not creating
threads but working on same ui thread. But there are some people
claiming that they are and there are also people claiming that
sometimes tasks creating threads and sometimes not. What is true here?
i've understood that when it comes to await keyword it will go back to the main context then go back to await (when it's finished) and then continue further if there's something after away within async method.
I don't think you're using the term "context" the way it's normally used when explaining async code. Check out my async intro blog post, which explains exactly what the context is and how it's used by await.
In particular, "context" does not mean "method or function". It does not determine how or when the code returns to another part of the code.
You may also want to read my MSDN article on async best practices, which explains why you should avoid async void (except for event handlers). If you make Form1_Load an async void and get rid of dfg, then it works as you would expect:
Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Await DoStafAsync
lblStatus.Text = "After DoStafAsync"
End Sub
The reason it wasn't working before is because Form1_Load was not waiting for dfg (or DoStafAsync) to complete - it was just firing them off and then proceeding immediately to setting the label text. As I point out in my best practices article, one of the main problems with async void (async sub) is that the calling code cannot determine when it has completed (at least, not easily).
I am having a function as below (kept only relevant code for this example):
Public Async Function RefreshCompanyCodesAsync() As Task(Of Boolean)
For ctr=1 to 2000
Await Task.Delay(1000).ConfigureAwait(False)
If ctr=5 Then Throw New ApplicationException("Why 5?")
Next
return true
End Function
As you can see, I am generating an exception when ctr = 5 just to check how the exception will be handled in the UI.
I then call this function inside a button click Async event:
Dim task1 = Task.Run(Sub()
RefreshCompanyCodesAsync()
End Sub)
Dim cTask = task1.ContinueWith(Sub(antecedent)
If task1.Status = TaskStatus.RanToCompletion Then
MsgBox("All done")
ElseIf task1.Status = TaskStatus.Faulted Then
MsgBox(task1.Exception.GetBaseException().Message)
End If
End Sub)
The task runs to completion instead of being faulted. I am guessing thats because a wrapper task runs to completion instead of the original task that threw an exception. However, am not sure how to get to the original task and handle the exception.
I have tried with Await.
Try
Await RefreshCompanyCodesAsync()
Catch ax As AggregateException
MsgBox(ax.Message)
Catch ex As Exception
MsgBox(ex.Message)
End Try
Exceptions get caught here, but the problem is I need to start multiple functionalities in parallel of which RefreshCompanyCodesAsync is only one of them. To do that, I believe launching those functionalities via Task.Run would be the best bet.
The actual functionality of the worker function in my case involves extensive DB and HttpClient activity and during this time, the UI responsiveness is driving me towards going in for Task.Run or Factory.StartNew
Also, have tried launching the task and Waiting for it so as to ensure evaluation of the result. However, my UI gets blocked when I do Task.Wait.
Any help is really appreciated.
Probably, what you want is
Dim task1 = RefreshCompanyCodesAsync()
That way there is no wrapper task. RefreshCompanyCodesAsync already is non-blocking which is important so that you don't freeze the UI.
You should avoid ContinueWith. Prefer await because it marshals you back to the UI thread.
I'll need to write this in C#:
var task1 = RefreshCompanyCodesAsync();
await Task.WhenAny(task1); //Wait without throwing.
MessageBox.Show(task1.Status.ToString());
How do you wait on a task from an async method, in a non-async method, without actually calling the method?
Class TestAsync
Dim workevent As New Threading.ManualResetEvent(False)
Sub work()
Dim Task As Task = Test()
'Do Work which affects Test Here
Threading.Thread.Sleep(100)
workevent.Set()
'wait for Test to complete
'Task.Wait() -causes the application to hang
End Sub
Async Function Test() As Task
workevent.WaitOne()
End Function
End Class
First, if possible, never do that. Synchronously waiting for an async method defeats the whole purpose of using async. Instead you should make work() into an Async Function WorkAsync() As Task. And then make the method(s) that call work() asynchronous too, and so on, all the way up.
Second, asynchronous isn't the same as parallel. If you have Async method with no Awaits, it will execute completely synchronously, as if it was a normal method. So, your method will hang even without Task.Wait().
What you can do is to run Test() on a background thread using Task.Run():
Sub work()
Dim Task As Task = Task.Run(AddressOf Test)
Threading.Thread.Sleep(100)
workevent.Set()
Task.Wait()
End Sub
The Task returned from Run() will complete when the Task returned from Test() completes.
(There are other issues causing hangs when combining Wait() and Async, but they are not relevant in this specific case.)
But I repeat: you shouldn't do this, since it blocks the UI thread, though it's only for a short while.