Safely aborting threads in a ThreadList - vb.net

A threadlist is being used so threads can be aborted at the user's request. Thus far, I ported some code from killing processes (also based on removing list items safely), but it is not aborting any of the threads. Using the try catch alone inside a loop from 0 To Threadlist.Count will abort the threads but will also result in an exception related to use of a list whose elements have been removed. Is there anything that I am doing wrong in the following code:
For x As Integer = threadList.Count - 1 To 0 Step -1
Dim tid As String = threadList(x).ManagedThreadId
For Each t As Thread In threadList
If tid = t.ManagedThreadId.ToString Then
Try
t.Abort()
threadList.RemoveAt(x)
Catch ex As ThreadAbortException
'ex.ToString()
End Try
End If
Next
Next

You can't remove an item from the list used in a For Each loop. Get the thread you want to remove and then remove it.
Dim threadToRemove As Thread = Nothing
' First, find the thread to remove
For Each t As Thread In threadList
If tid = t.ManagedThreadId.ToString Then
threadToRemove = t
Exit For
End If
Next
' Then, remove the thread
If threadToRemove IsNot Nothing Then
Try
t.Abort()
threadList.Remove(threadToRemove)
Catch ex As ThreadAbortException
'ex.ToString()
End Try
End If
By splitting your logic it will be ok. You can then put those two pieces in method if you want.
I don't know if this piece of code will fix your problem but I hope you get the idea. Looping your threadList twice will just remove all thread in a complicated way.

Related

Looped Tasks Submitted Inside Try/Catch Blocks

I have a background task "submitter" that handles one or more scheduled tasks:
...
While sdr.read()
...
Dim oBackground As New Background
Task.Run(Sub() CallByName(oBackground, sProcessKey, CallType.Method, iPQID))
End While
Before you ask, yes, the tasks are thread-safe. :)
I want the submitter to end (go back to sleep) while the tasks operate; awaiting the next heartbeat, but I need to catch and handle exceptions in each thread.
The only way I've come up with to do this is to create a new task for each background job to be run and handle the exception there, but this seems inefficient:
Create new Task for each background job that then, submits the actual job and waits to see if an error happened and handles it:
Private Sub NewJob(sProcessKey, iPQID)
Dim t As Task
....
While sdr.read()
...
Dim oBackground As New Background
t = Task.Run(Sub() CallByName(oBackground, sProcessKey, CallType.Method, iPQID))
Try
t.Wait()
Catch ex As AggregateException
For Each IEx As Exception In ex.InnerExceptions
HandleBackgroundException(IEx, sProcessKey)
Next
End Try
End While
End Sub
Isn't there a better way to do this??
Thanks!
I believe I've found the answer to my own question.
Since each task is it's own thread, no additional thread submittal is needed.
The task is run directly and error handling happens within the task as shown above (but without Await'ing it). Thus:
Public Shared Sub MyTask()
Try
... do some work here ...
Catch ex As AggregateException
For Each IEx As Exception In ex.InnerExceptions
HandleBackgroundException(IEx, sProcessKey)
Next
End Try
End Sub
That's it!
I hope this helps someone else.

vb.net Not waiting until all tasks have completed

So I'm processing records. I'm using a task to process each record.
My issue is that my program is completing before all tasks complete.
Anyone any thoughts on what I'm doing wrong here?
Dim task As Task
Try
'Keep looping until no more requests to run have been made
Do
Dim controller As New Controller()
Dim record As Record = controller.GetNextRecord()
If record IsNot Nothing Then
'Use Task!
task = Task.Factory.StartNew(Sub() controller.ProcessRecord(record), TaskCreationOptions.LongRunning)
CalledWhenBusy = True
End If
TryAgain:
Loop Until ProcessAgain() = False
Catch ex As System.Net.WebException
logger.ErrorException("unable to connect to remoting server", ex)
Finally
logger.Info("Processed all records.. now about to wait for all tasks to complete")
'Wait till all tasks have stopped running
Task.WaitAll(task)
logger.Info("Processed all records.. All tasks have completed")
'The dispatcher has finished for now so clean up
Me.StopUsing()
End Try
Private Function ProcessAgain() As Boolean
If CalledWhenBusy Then
'Reset the flag and exit with true
CalledWhenBusy = False
Return True
End If
Return False
End Function
UPDATE
I've resolved my issue by using a list of tasks as suggested by #HansPassant and #usr
The reason for not using Foreach, is that more records can be added while processing.. hence the do while loop...
Thank you for your help.
Dim taskList = New List(Of Task)()
Try
'Keep looping until no more requests to run have been made
Do
Dim controller As New Controller()
Dim record As Record = controller.GetNextRecord()
If record IsNot Nothing Then
'Use Task!
taskList.Add(Task.Factory.StartNew(Sub() controller.ProcessRecord(record)))
CalledWhenBusy = True
End If
TryAgain:
Loop Until ProcessAgain() = False
Catch ex As System.Net.WebException
logger.ErrorException("unable to connect to remoting server", ex)
Finally
logger.Info("Processed all records.. now about to wait for all tasks to complete")
'Wait till all tasks have stopped running
Task.WaitAll(taskList.ToArray())
logger.Info("Processed all records.. All tasks have completed")
'The dispatcher has finished for now so clean up
Me.StopUsing()
End Try
Task.WaitAll(task) is just waiting for one task. Where are the others? Did you even store them? Not apparent from this code.
Ideally, you transform this code so that it can make use of Parallel.ForEach. You need to put the work items into IEnumerable format for that to work. For example, add them to a List and feed the list to Parallel.ForEach.

How do I solve 'System.OutOfMemoryException'

I have a Windows Service application. It is a very busy application. It is supposed to run continuously looking for things to do. After it runs for a while I get
Exception of type 'System.OutOfMemoryException' was thrown.
It can happen at different times but usually a this paragraph:
Private Shared Function GetUnprocessedQueue() As Boolean
Try
Dim l_svcOOA As New svcITGOOA.IsvcITGOOAClient(OOAProcessing.cGlobals.EndPoint_ITGOOA)
Dim l_iFilter As New svcITGOOA.clsFilter
With l_svcOOA
With l_iFilter
.FilingType = OOAProcessing.cGlobals.FilingType
End With
m_ReturnClass = .itgWcfOOA(1, cGlobals.DatabaseIndicator, svcITGOOA.eOOAAction.GetUnprocessedQueue, l_iFilter, 71)
Return CompletedGetUnprocessedQueue(m_ReturnClass)
End With
Catch ex As Exception
ExceptionHandling(ex, "GetUnprocessedQueue " & m_Application)
Return False
End Try
End Function
This is using a wcf service to read a queue. It reads the queue every two minutes to see if new records have been added to it.
Please help me solve this. I don’t know where to start.
The OutOfMemoryException exception occurs when the GC has completed a cycle of collection but the memory is not available even after that. I couldn't make out what the above code snippet does, but I think using Weak References for objects could be useful.
I had a timer that was generated within the same paragraph that I was setting
For example
m_svcTimer = New Timers.Timer With {.Interval = m_Interval, .Enabled = True}
AddHandler m_svcTimer.Elapsed, AddressOf StartTheQueueIfTime
m_svcTimer.Enabled = True
m_svcTimer.Start()
was within the paragraph StartTheQueueIfTime. I thought this would be a way to change the time interval. Instead it kept creating new events. Finally too many caused my crash.
Bob

Better way to retry a statement that threw an exception in vb.net

I usually do something like this:
Dim Attempts = 0
Try
Retry:
<Block>
Catch
If Attempts < 3 Then
Attempts += 1
Thread.Sleep(2000)
GoTo Retry
Else
Throw
End If
End Try
This is really bad looking for me, but i don't know of a better way of doing it.
You could also try the following:
Dim retryCount as Integer = 0
Dim wasSuccessful as Boolean = False
Do
Try
<statements>
'set wasSuccessful if everything was okay.'
wasSuccessful = True
Catch
retryCount +=1
End Try
Loop Until wasSuccessful = True OrElse retryCount >=5
'check if the statements were unsuccessful'
If Not wasSuccessful Then
<do something>
End If
It will retry up to five times if the statements were not successful but will immediately exit the loop if the statements' execution was successful.
I think that's a bad usage, I use this one, and it's much cleaner.
Dim maxAttempt As Integer = 2
For i As Integer = maxAttempt To 0 Step -1
Try
...
'Successful Quit
Exit For
Catch
Thread.Sleep(2000)
End Try
Next
Just use a For loop or a While loop rather than GoTo, breaking on success. But other than that, it's the right approach.
Conceptually it's the right approach, although I would not catch each and every exception, see answer by #0xA3.
You could make it a bit 'prettier' by separating the retry logic from the actual code, e.g.:
Sub TryExecute(Of T As Exception)(ByVal nofTries As Integer,
ByVal anAction As Action)
For i As Integer = 1 To nofTries - 1
Try
anAction()
Return
Catch ex As T
Thread.Sleep(2000)
End Try
Next
' try one more time, throw if it fails
anAction()
End Sub
Which could then be used like so:
TryExecute(Of SomeExceptionType)(3, Sub()
<Block>
End Sub())
This will only work in VB 10, if you're using .Net 3.5 / VB 9, you need to separate this in a separate function
In general, retrying something that failed should be considered very carefully. Usually it is much better to report the error and let the user decide.
Raymond Chen gives a nice example how automatic retries might lead to unwanted problems and gives the advice to avoid retrying:
Take it easy on the automatic retries

While loop causes the app to go slow? Any idea why?

I have a simple code that looks up a text file, reads the line of text, splits the string by semi-colons and then posts the results.
After it has done this, I have created a really simple while loop to waste 10 seconds before going for it again.... here is the code:
Private Sub checkTemps()
While Abort = False
Try
fileReader = New StreamReader(directory.Text & "currentTemp.dat")
rawData = fileReader.ReadLine()
fileReader.Close()
Dim dataArray() As String
dataArray = rawData.Split(";")
updateOutput("1", dataArray(0), dataArray(1))
updateOutput("2", dataArray(2), dataArray(3))
updateOutput("3", dataArray(4), dataArray(5))
updateOutput("4", dataArray(6), dataArray(7))
stpWatch.Start()
While stpWatch.Elapsed.Seconds < 10 And Abort = False
pollInterval(stpWatch.ElapsedMilliseconds)
End While
stpWatch.Stop()
stpWatch.Reset()
Catch ex As Exception
msgbox("oops!")
End Try
End While
closeOnAbort()
End Sub
But when it gets to the "time-wasting" loop - it seems to slow the whole application down? And I can't work out why!
So a couple of questions... is there a better way to do all this? and second - can anyone spot a problem?
All the other commands seem to run fine - there isn't much else to this app. I have another program that updates the dat file with the values, this is simply a client side app to output the temperatures.
Any help would be appreciated.
Andrew
More info:
I should explain what the pollInterval sub does!
Private Delegate Sub pollIntervalDelegate(ByVal value As Integer)
Private Sub pollInterval(ByVal value As Integer)
If Me.InvokeRequired Then
Dim upbd As New pollIntervalDelegate(AddressOf pollInterval)
Me.Invoke(upbd, New Object() {value})
Else
ProgressBar1.Value = value
End If
End Sub
Your loop is a very tight loop continually calling pollInterval. This will tie up the application until the loop condition is met.
You should use the Sleep method to pause this thread for the required amount of time.
If you want to show the progress (as per your update) you could put the Sleep into the loop and sleep for 1 second (or half a second?) at a time:
While stpWatch.Elapsed.Seconds < 10 And Abort = False
Sleep(1000) <-- NOT 100% sure of the syntax here,
but the time is specified in milliseconds
pollInterval(stpWatch.ElapsedMilliseconds)
End While
You should go with
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(10).TotalMilliseconds);