Destroy Thread with 'aborted' state - vb.net

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

Related

Async reading from process gets paused

I have made an application in vb.net which launches a cmd like process in the background, reads its output asynchronously and redirects it in a textbox. Normally my application should show every new output line in the textbox in real time. Unfortunately here is what happens:
These are the first output lines which are shown in the textbox.
After a few seconds an unwanted pause occurs for some reason. No new lines are shown in the textbox even though there is output from the process. After a few minutes all that output i couldn't see because of the pause, shows up and then a pause occurs again.
Finally, the last bunch of lines show up and the process is terminated.
Here is my code:
Private Sub StartSteamCMD()
Try
Dim psi As ProcessStartInfo = New ProcessStartInfo("cmd.exe", "/C steamcmd +runscript " + temp + "Setti_SRCDS_Manager\install_update.txt")
With psi
.UseShellExecute = False
.RedirectStandardInput = True
.RedirectStandardOutput = True
.RedirectStandardError = True
.CreateNoWindow = True
.StandardOutputEncoding = Encoding.GetEncoding(CultureInfo.CurrentUICulture.TextInfo.OEMCodePage)
.StandardErrorEncoding = Encoding.GetEncoding(CultureInfo.CurrentUICulture.TextInfo.OEMCodePage)
End With
process = New Process With {.StartInfo = psi}
AddHandler process.OutputDataReceived, AddressOf Async_Data_Received
AddHandler process.ErrorDataReceived, AddressOf Async_Data_Received
process.Start()
process.BeginOutputReadLine()
process.BeginErrorReadLine()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub Async_Data_Received(sender As Object, e As DataReceivedEventArgs)
Try
Invoke(New InvokeWithString(AddressOf Sync_Output), e.Data)
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub Sync_Output(text As String)
Try
TextBox1.AppendText(text + Environment.NewLine)
TextBox1.ScrollToCaret()
If Not String.IsNullOrEmpty(text) Then
If text.Contains("downloading") Or text.Contains("validating") Then
<code to animate the progress bar here>
ElseIf text.Contains("Success") Then
<the server is installed successfully, some code to run here>
ElseIf text.IndexOf("error", 0, StringComparison.CurrentCultureIgnoreCase) > -1 Then
<some code to run in case of error>
End If
End If
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Any thoughts of what can cause those pauses? I have been thinking the following. As you can see in the first screenshot, the last two lines are about establishing a connection with Steam servers (the place where the game server files will be downloaded from) and waiting for user info. Maybe that moment the process doesn't produce any output for a few seconds. So at some point during those seconds, the first pause of async reading occurs. Then the process starts producing output again but it's not redirected to the textbox because of the pause. Could it be the async reading gets paused because of the process not producing any output for a few seconds?
I don't know what else to think. I'm tired of searching the internet for a solution, because i can't find any post about my problem or at least something similar. I hope you guys can help me out. Thanks in advance

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

Exception handling in TPL

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.

Thread to update DataGridView

I have a windows form with a button and a datagridview. When I select the button a thread is initiated that does something and eventually fills my datagridview. The problem is that the datagridview is not filled.
What am I doing wrong?
My code:
Private MyCreateStructureFromSampleXMLTread As Thread
Private Sub ButtonCreateStructureFromSampleXML_Click(sender As Object, e As EventArgs) Handles ButtonCreateStructureFromSampleXML.Click
Me.Cursor = Cursors.WaitCursor
MyCreateStructureFromSampleXMLTread = New Thread(AddressOf ModuleXML_MESSAGE_STRUCTURE.CreateStructureFromSampleXML)
MyCreateStructureFromSampleXMLTread.IsBackground = True
MyCreateStructureFromSampleXMLTread.SetApartmentState(ApartmentState.STA)
MyCreateStructureFromSampleXMLTread.Start()
Me.Cursor = Cursors.Default
End Sub
Threading:
Friend Sub CreateStructureFromSampleXML()
FormUTool.Cursor = Cursors.WaitCursor
Try
If ModuleFileHandling.OpenFile(Application.StartupPath, "Select XML File", ".xml", "XML File (*.xml)|*.xml") = True Then
ParseXMLFile(PublicUToolVariable.MyOpenedFile)
End If
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
End Try
ModuleXML_MESSAGE_STRUCTURE.AlignElementPaths(PublicUToolVariable.MyAccessConnection)
ModuleXML_MESSAGE_STRUCTURE.XML_MESSAGE_STRUCTUREFillDataGridView(PublicUToolVariable.MyAccessConnection)
FormUTool.Cursor = Cursors.Default
End Sub
You are trying to fill the DataGridView from a thread other than the UI thread. That is what you are doing wrong. UI elements have thread affinity requirements that mandate that they are only ever accessed from the thread that created them.
Change your strategy so that you are loading and parsing the XML in a worker thread and then send that data to the UI thread and let the UI thread fill the DataGridView. That is the standard approach. You can use the BackgroundWorker class. The DoWork event handler will execute on a worker thread and the RunWorkerCompleted event handler will run on the UI thread so all of the marshaling is handled for you.

How to avoid Thread.Abort() in this case?

I know that Thread.Abort() is not really safe, but I cannot imagine what it could cause in case below:
Private threadLoadList As Thread
Private Sub LoadList(ByVal argument As Integer)
Try
Refresh_Grid(GET_DATA(argument), argument) 'Fills grid with data loaded from database
Change_Cursor() 'Changes cursor to Cursor.Default
Catch ex As ThreadAbortException
'Do nothing
Catch ex As Exception
Error_log(ex.Message) ' Saves ex.Message in database
End Try
End Sub
Private Sub SomeValueChanged(sender As Object, e As EventArgs) Handles Control.ValueChanged
If Not threadLoadList Is Nothing Then
If threadLoadList.IsAlive Then
threadLoadList.Abort()
End If
End If
Cursor = Cursors.WaitCursor
threadLoadList = New Thread(AddressOf LoadList)
threadLoadList.Start(1)
End Sub
As you can see, user can change some value (ComboBox) and in result change content of the grid. Function GET_DATA() takes around 10 seconds, thus it is possible that user changes value of Combobox before grid is refreshed - that is why previously started thread is killed.
Is it dangerous? If yes, could you propose some other solution?
Ok, I understand ;). I try to avoid timeouts (in some cases query executes below 1 second) Is it better solution:
Private threadLoadList As Thread
Private Changed As Boolean = False
Private Sub LoadList(ByVal argument As Integer)
Try
Dim dt As DataTable = GET_DATA(argument)
'Enter Monitor
If Changed Then
Return
End IF
'Exit Monitor
Refresh_Grid(dt, argument) 'Fills grid with data loaded from database
Change_Cursor() 'Changes cursor to Cursor.Default
'Enter Monitor
Changed = False
'Exit Monitor
Catch ex As ThreadAbortException
'Do nothing
Catch ex As Exception
Error_log(ex.Message) ' Saves ex.Message in database
End Try
End Sub
Private Sub SomeValueChanged(sender As Object, e As EventArgs) Handles Control.ValueChanged
'Enter Monitor
Changed = True
'Exit Monitor
Cursor = Cursors.WaitCursor
Dim t As Thread = New Thread(AddressOf LoadList)
t.Start(1)
End Sub
First things first, unless there is extra code in Refresh_Grid that I am not seeing it looks like you are attempting to modify a UI element from a thread other than the UI thread. That is a big no-no. UI elements are designed to be accessed only from the UI thread so attempting to modify one on a worker thread will lead to unpredictable behavior.
In regards to your usage of Thread.Abort the answer is yes; it is dangerous. Well, it is dangerous in the sense that your application will throw an exception, corrupt data, crash, tear a hole in spacetime, etc. There are all kinds of failure modes that might be expected from aborting a thread. The best solution is to use a cooperative termination protocol instead of doing a hard abort. Refer to my answer here for a brief introduction on valid approaches.