Exception handling in TPL - vb.net

I am using the following code for creating some files.
I have observed that sometimes if some exception occurs all Parallel.For threads stop in between.
I have few questions.
Should I be using AggregateException in CreateReport method or its # rt place.
How to make sure that if exceptions arises in any of the threads it does not stop other parallel threads.
Try
dtScheduledReports = objReprotHelper.GetTopImmediateReportsForExecution()
Parallel.For(0, dtScheduledReports.Rows.Count, Sub(i)
CreateReport(dtScheduledReports.Rows(i))
End Sub)
Catch ae As AggregateException
For Each ex As Exception In ae.InnerExceptions
ExceptionHandler.LogError(ex)
Next
End Try
Private Sub CreateReport(dtRow As DataRow, scheduleType As Integer)
Try
//do something
Catch
throw
End Try
End Sub

You can use a ConcurrentQueue(Of Exception) to enable safe exception enqueueing from multiple threads. This allows to execute the entire loop and throws all exceptions in an AggregateException.
Private Sub DoAParalelJobAndThrowErrors
Dim exQueue as New ConcurrentQueue(Of Exception)
dtScheduledReports = objReprotHelper.GetTopImmediateReportsForExecution()
// Execute the complete loop and capture all exceptions.
Parallel.For(0, dtScheduledReports.Rows.Count, Sub(i)
Try
CreateReport(dtScheduledReports.Rows(i))
Catch ex as Exception
exQueue.Enqueue(ex)
End Try
End Sub)
If exQueue.count > 0 throw new AggregateException(exQueue)
End Sub
Private Sub CreateReport(dtRow As DataRow, scheduleType As Integer)
//do something
End Sub
According MSDN this way doen't break the loop, all rows should be processed.

Related

Timers and variables in vb.net

I have a class with a timer. The timer starts in the new method. The method associated with the timer is checkMatch() but checkTimer() ensures the parameters are met to start the timer. They are met and the timer starts. The method checkMatch() is currently rewritten to only execute once since I know it keeps repeating itself. The only place to change the boolean variable oneTime is located inside of checkMatch(). I included the autoLoad() function just in case it is needed. Whenever I launch this class, the forms associated with the other methods not listed will pop up and instantly go away. The console shows that checkMatch() is being called regularly (which it should), but that the variable oneTime remains True each time it is called. The timer is started at the end of the autoLoad() method. I know all the methods in autoLoad() are being called and executed.
Since oneTime is showing as True constantly in the console, I am assuming I have a conception issue somewhere. Why is oneTime true each time checkMatch() is called?
' a user is required for this class
Public Sub New(ByVal my_user As RGOUser)
My.Settings.selected_summoner = "huge legend"
My.Settings.Region = "na1"
My.Settings.Save()
myUser = my_user
AddHandler tmr.Elapsed, AddressOf checkMatch
checkTimer()
End Sub
Public Sub checkMatch()
Console.WriteLine(Me.GetType.ToString() + "||" + System.Reflection.MethodInfo.GetCurrentMethod.ToString())
Console.WriteLine(oneTime)
Try
Dim psList() As Process = Process.GetProcesses()
For Each p As Process In psList
' If strLeagueProcessName = p.ProcessName) And Not boolInGame Then
' remove later and replace with commented line
If oneTime Then
Console.WriteLine("checkMatch Success.")
boolInGame = True
oneTime = False
tmr.Interval = timerInsideMatch
tmr.Stop()
Try
autoLoad()
Catch ex As Exception
End Try
Return
Else
Return
End If
Next
Catch ex As Exception
Console.WriteLine("checkMatch Failure. " + ex.Message)
End Try
boolInGame = False
tmr.Interval = timerOutsideMatch
End Sub
Private Sub autoLoad()
Console.WriteLine(Me.GetType.ToString() + "||" + System.Reflection.MethodInfo.GetCurrentMethod.ToString())
If searchMatch() Then
Dim x As New System.Threading.Thread(AddressOf loadConfiguration)
Dim y As New System.Threading.Thread(AddressOf loadMatch)
Dim z As New System.Threading.Thread(AddressOf launchMinimap)
If My.Settings.configuration_auto_load Then
Try
x.Start()
Catch ex As Exception
End Try
End If
If My.Settings.loadMatch Then
Try
y.Start()
Catch ex As Exception
End Try
End If
If My.Settings.launchMinimap Then
Try
z.Start()
Catch ex As Exception
End Try
End If
x.Join()
y.Join()
z.Join()
tmr.Start()
End If
End Sub

Destroy Thread with 'aborted' state

Using background worker in a winform program. Also communicating with somedevices
I have a stop button which tries to stop the background worker thread, which works, but sometimes, the background worker thread remains in state "Aborted"
I have to mention that I take care about the exception that rise, and also use a 'Finally' block to stop the communication with the devices
I need to stop the thread immediately, something like an emergency button...
Some code:
Private Sub BtnStopTest_Click(sender As Object, e As EventArgs) Handles btnStopTest.Click
Try
stoppedTesting = True
Log("Stopping operations safely. (You might have to wait some time )", Color.Blue, New Font("Microsoft Sans Serif", 9, FontStyle.Bold))
If bgWorkThread IsNot Nothing Then
'stop thread
'if thread is sleeping (waiting for a time)
If bgWorkThread.ThreadState = ThreadState.Background + ThreadState.WaitSleepJoin Then
bgWorkThread.Interrupt()
Else 'if thread is working normally
bgWorker.CancelAsync()
tEO.DoWorkEventArgs.cancel = True
bgWorkThread.Abort()
'sometimes, here the Thread has state 'Aborted
End If
ElseIf bgWorkThread Is Nothing Then
Dim ee As New System.ComponentModel.RunWorkerCompletedEventArgs(New Object, Nothing, False)
BgWorker_RunWorkerCompleted(New Object, ee)
End If
Catch ex As Exception
Utils.PreserveStackTrace(ex)
Log("Error when stopping testing" & vbCrLf & Utils.ReadException(ex), MessageType.ErrorMessage)
End Try
End Sub
Private Sub BgWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bgWorker.DoWork
Try
'some other things to do
For Each testStep In stepList
Try
'main operations and communication with device
' below functions are all different
'something like:
'CommunicationWithDevice1()
'CommunicationWithDevice2()
'CommunicationWithDevice3()
'CommunicationWithDevice4()
'....
'CommunicationWithDevice20()
Catch ex As Exception When TypeOf ex Is ThreadAbortException OrElse TypeOf ex Is ThreadInterruptedException
Utils.PreserveStackTrace(ex)
Log("Exception in thread" & vbCrLf & Utils.ReadException(ex), MessageType.ErrorMessage)
e.Cancel = True
If ex.GetType = GetType(ThreadAbortException) Then Thread.ResetAbort()
If stoppedTesting Then Exit For
Catch ex As Exception
If stoppedTesting Then Exit For
End Try
Catch ex As Exception When TypeOf ex Is ThreadAbortException OrElse TypeOf ex Is ThreadInterruptedException
e.Cancel = True
Log("Background worker thread was interrupted!")
Log("Background worker thread was interrupted!", Color.Red, New Font("Microsoft Sans Serif", 9, FontStyle.Bold))
Catch ex As Exception
Utils.PreserveStackTrace(ex)
Log("Error when doing background work!" & vbCrLf & Utils.ReadException(ex), Color.Red, New Font("Microsoft Sans Serif", 9, FontStyle.Bold))
Finally
StopCommunication()
End Try
End Sub
What can I do to completely destroy the thread?
If there is not possibility, any workaround to exit my 'DoWork' method immediately?
BackgroundWorker.CancelAsync() does not cancel the thread immediately, it actually posts the STOP message on the queue as you can check the BackgroundWorker.isCancellationPending is set to true, if you want to cancel immediately, you could have a global boolean and set it if you can to cancel the thread. In your Do_Work() method, you can regularly check the boolean and if it is set, run Exit Sub.
Something like this should do
Else 'if thread is working normally
cancelThread = true
tEO.DoWorkEventArgs.cancel = True
and inside your Do_Work sub
For Each testStep In stepList
Try
If cancelThread Then
Exit Sub
This will cleanly stop the BackgroundWorker and allow you to run the RunWorkerCompleted routine. Inside it, you can check for the cancelThread to know if the BackgroundWorker ran successfully or it was aborted.
UPDATE 2
After going through your code one more time, why do you need to use BackgroundWorker and Thread together? If you want immediate cancellation, use only Thread. Also, you only need one TRY..Catch for the entire function, that way it will ensure that the thread exits. You can always assign the thread to nothing and Garbage Collector will take care of freeing resources for you(If you are concerned with ABORTED)
Dim myThread As Thread = new Thread(new ThreadStart(yourFunctionForCommunication))
myThread.Start()
And in your function
Try
.. Do your work
Catch Ex As ThreadAbortException
Exit Sub;
Finally
//Do your cleanup code here
End Try
The abort call
myThread.Abort()
While mythread.ThreadState <> ThreadState.Aborted
End While
myThread = Nothing
But I should warn you that Aborting threads is not a safe process. See this MSDN Post.
If you would still want to use Background worker, i would suggest you to look into List<dynamic>. Add all the Communicatewithdevice1()....to 20() there then use that in a loop, that way you would not have to write the If conditional 20 times. See the post Here

Throwing exception from sub to async call

This is a windows forms application in which I have a particular form. On this form I display the progress of some processing that is supposed to happen in the background asynchronously. All of it works great, except for when I try to handle exceptions that are caught within the background processing....
This is the sub in my form's code that calls the Async function, which is in a module containing all the background processing code:
Public Async Sub BasicProcessing()
Try
Dim processTarget As Action(Of Integer)
processTarget = AddressOf UpdatePulseProcessing
myProgress = New Progress(Of Integer)(processTarget)
myCount.Vehicles = Await ProcessmyCountFile(myCount, myProgress)
If OperationCanceledByUser = True Then
Exit Sub
End If
Catch ex As Exception
MessageBox.Show(Me, "Unable to update count." _
& Environment.NewLine & ex.Message, _
"Error updating count", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
End Try
End Sub
This is the async function that it calls, which is in a separate module:
Public Function ProcessmyCountFile(CountToProcess As Count, ByVal ProgressObject As IProgress(Of Integer)) As Task(Of List(Of Vehicle))
myProgressObject = ProgressObject
basicToken = New CancellationTokenSource
Try
Return CType(Task(Of List(Of Vehicle)).Run(Function()
If basicToken.IsCancellationRequested Then
Return Nothing
Exit Function
End If
myCountFile = CountToProcess
MyVehicles = New List(Of Vehicle)
'All that is important in here to note is a call to a regular sub within this module
CreateVehicles()
Return MyVehicles
End Function, basicToken.Token), Global.System.Threading.Tasks.Task(Of List(Of Global.STARneXt.Vehicle)))
Catch ex As Exception
Throw New Exception(ex.Message)
Return Nothing
End Try
End Function
Public Sub StopProcess()
If Not basicToken Is Nothing Then
basicToken.Cancel() ' We tell our token to cancel
End If
End Sub
This is the regular sub called by the Async function:
Private Sub CreateVehicles()
Try
'In here are calls to other regular subs within the same module, let's just call them A and B
Catch ex As Exception
StopProcess()
Throw New Exception("Error creating vehicles at pulse " & pulsePointer & ". " & ex.Message)
End Try
End Sub
When I run this code with data that I know ends up generating an error in sub B, the error does propagate up, as far as up to the method that is directly called by the async function....
So when running in VS, it will stop at "Throw New Exception("Error creating vehicles at pulse " & pulsePointer & ". " & ex.Message)", with the Message containing the message thrown by sub B.
This is what the debugger says on that line:
An exception of type 'System.Exception' occurred in MyProject.exe but
was not handled in user code. Additional information: Error creating
vehicles at pulse....[error message propagated up as thrown by called
subs]. Arithmetic operation resulted in an overflow.
Then strangely enough, within the debugger if I hit "Step Into", it does then return to the sub in my form that called the Async function, which shows the message box on the GUI.
So how do I get this to automatically return back up to the original form code to show the message box? Why does it stop where it stops without continuing to propagate?
FYI, I did find this question, but it ultimately did not help.
#StephenCleary was right - I created a new install for my project as it is, and in the installed version I do get the message box with the expected error message.
Strange behavior on the part of the debugger, and a bit discouraging, but none-the-less I am glad that my code as laid out in my question does actually work.

server busy error message vb.net

I get this error. I can not catch it where it comes from. All methods made with try catch but still I get this error from time to time. I used code:
Private Sub add2ComboBox_called(ByVal text As String)
Try
If Me.InvokeRequired Then
Dim args() As String = {text}
Me.Invoke(New Action(Of String)(AddressOf add2ComboBox_called), args)
Return
End If
ComboBox_called.Items.Add(text)
Catch ex As Exception
log_it(ex.StackTrace)
End Try
End Sub
and also this one:
Public Sub clear_do_not_open()
Dim FILENAME As String = path & "\DO_NOT.OPEN"
Dim index As Integer
Try
Using fs As New IO.FileStream(FILENAME, IO.FileMode.Truncate, IO.FileAccess.Write, IO.FileShare.ReadWrite), _
tl As New TextWriterTraceListener(fs)
index = Trace.Listeners.Add(tl)
Trace.Write("")
Trace.Listeners(index).Flush()
Trace.Flush()
End Using
Trace.Listeners.RemoveAt(index)
Catch ex As Exception
log_it(ex.StackTrace)
End Try
End Sub
How do I make this to press Retry by default if it appears? Wait for a second and press Retry again? If not like this then how do I know that this message pops up? If catch the moment it shows up I could then just restart the program or send an email to myselt so I could come to PC where program runs and I could handle it. Now message pops up and program stops while its on.

How to handle errors in a cross thread operation

I have a webbrowser control created at runtime and used via a background thread. The following is an example of the code used:
If Me.InvokeRequired Then Me.Invoke(Sub() webbroswers(3).Document.GetElementById("ID").InnerText = TextBox4.Text)
This works great! But sometime the webbrowser doesn't have the element "ID" (for example). So I'd like a method to basically allow the code to continue if an error occurs. I've tried the try - catch block but this doesn't catch it!
You can use a multi-line lambda expression, like this:
If Me.InvokeRequired Then Me.Invoke(
Sub()
Dim element As HtmlElement = webbroswers(3).Document.GetElementById("ID")
If element IsNot Nothing Then
element.InnerText = TextBox4.Text
End If
End Sub
)
Checking if it's Nothing, like that, is more efficient than letting it fail and catching the exception. However, if you need to do a Try/Catch for any other reason, you can also do that easily in a multi-line lambda expression, for instance:
If Me.InvokeRequired Then Me.Invoke(
Sub()
Try
webbroswers(3).Document.GetElementById("ID").InnerText = TextBox4.Text
Catch ex As Exception
' ...
End Try
End Sub
)
However, if the lambda expression gets too long, or if you'd like to have more meaningful stack traces in your exceptions, you can use a delegate to an actual method, like this:
If Me.InvokeRequired Then Me.Invoke(AddressOf UpdateId)
'...
Private Sub UpdateId()
Try
webbroswers(3).Document.GetElementById("ID").InnerText = TextBox4.Text
Catch ex As Exception
' ...
End Try
End Sub