So I have 2 threads in my application which work fine except for the fact that they will continue taking up rather large amounts of my processor even after they are closed or aborted. This is how one of the loops look like along with some of the main declarations.
Dim Thread1 As System.Threading.Thread
Dim ThreadRunning As Boolean
Dim Thread1Running As Boolean = False
Sub Something()
While True
If ThreadRunning = True Then
Try
...Actions which don't necessarily affect the thread
Catch ex As Exception
End Try
ElseIf ThreadRunning = False Then
If Thread1Running = True Then
Thread1Running = False
Thread1.Abort()
Else
End If
End If
End While
End Sub
Here is the code I used to start the thread.
Thread1Running = True
Dim Thread1 As New System.Threading.Thread(AddressOf Something)
Thread1.IsBackground = True
Thread1.Priority = ThreadPriority.Lowest
Thread1.Start()
Here is the code I use to stop the thread.
ThreadRunning = False
The actions in the thread need threads since timers were too slow for the task(even at a 1 ms interval). The actions are performed fine except for when I abort the thread. In this case the thread will close and the CPU usage will go from around 25% to 0% for this program but then it will crash with a CLR error. When I aborted the thread from outside the sub it would still leave the program CPU usage at 25% which is what I'm trying to avoid. So my main question is: Is it possible to close the thread and reopen it later without it crashing, and so that while it is closed it won't use up 25% of the CPU(or some other rather high CPU performance)? If this isn't enough information I will provide more but I thing this will suffice... hopefully. Thanks for any help.
Here's the solution I found which worked.
While True
If ThreadRunning = True Then
Try
...Code stuff
Catch ex As Exception
End Try
ElseIf ThreadRunning = False Then
Exit While
End If
End While
Exit Sub
Apparently checking it this way made it so that 1. The thread doesn't hang. 2. It fully exits the thread and prevents it from using the CPU while closed. And 3. It allows for reusing the threads through the same method as shown above.
Related
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.
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.
Sorry for the messy code :S
If CheckBox2.Checked = True Then
For i As Integer = 0 To 1 Step 0
If CheckBox1.Checked = True Then
If TextBox1.Text = lblCLickLImit.Text Then
Timer1.Stop()
TextBox1.Text = "0"
System.Windows.Forms.SendKeys.Send("{F5}")
System.Threading.Thread.Sleep(delaydelaytime)
System.Windows.Forms.SendKeys.Send("{ENTER}")
Else
If CheckBox1.Checked = False Then
If TextBox1.Text = lblCLickLImit.Text Then
Timer1.Stop()
TextBox1.Text = "0"
End If
End If
End If
Else
If CheckBox2.Checked = False Then
If CheckBox1.Checked Then
If TextBox1.Text = lblCLickLImit.Text Then
Timer1.Stop()
TextBox1.Text = "0"
System.Windows.Forms.SendKeys.Send("{F5}")
System.Threading.Thread.Sleep(delaydelaytime)
System.Windows.Forms.SendKeys.Send("{ENTER}")
End If
Else
If CheckBox1.Checked = False Then
If TextBox1.Text = lblCLickLImit.Text Then
Timer1.Stop()
TextBox1.Text = "0"
End If
End If
End If
End If
End If
Next
Basically this code is for an Auto Clicker program,(Hopefully this will help you understand, http://prntscr.com/7tuc3o interface) Ok so when the "Continuous" checkbox is selected the code is in theory supposed to loop for infinity. However when I run the program with everything selected as shown all that happens is the program clicks once and then crashes (not responding). Any help I have tried this loop in other programs and it works, just not with this code.
Your loop is tying up the UI thread. You'll need to look into using either a background worker:
BackgroundWorker handles long-running tasks. It does not freeze the entire program as this task executes.
(dotnetperls.com)
Here is the msdn walkthrough of how to set-up a backgroundworker:
https://msdn.microsoft.com/en-us/library/ywkkz4s1.aspx
Or
If this is a personal project and no one you love will need to maintain this code, you can use Application.DoEvents() to continue to pump messages while the program is looping. Here is the msdn documentation for that https://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents(v=vs.110).aspx
First of all, a Step of 0 doesn't really make any sense in a for loop. It may /work/ but it is going to drive anyone reading it later insane. If you want an infinite loop don't use a for loop, use:
While True
'code here
End While
For loops are used when you know exactly how many iterations of your loop you need. A while loop is designed to iterate as long as some condition is true. In this example the condition is always true so it loops indefinitely.
Your code is also just going to spin the UI thread constantly. It never pauses for input (even your sleep calls do not release the thread for input). As far as the OS knows, your app has locked up because it never processes any of the window messages that get posted to it. It is still happily spinning away though, until windows finally gets tired of it and prompts you to kill it.
Not sure what other "programs" you're working with but I can tell you you don't want to use a For loop for this. You want a do/while loop, like either:
While True
...
End While
or
Do
...
Loop While True
Can someone show/tell me where I'm steering wrong with this? I am running a second thread in my application where all it does is read values from registers in a motion controller and then constantly updates those values to the appropriate fields on my UI every 100ms. When I disconnect the controller (ie...Ethernet cable disconnected, so lost comms), I want to destroy/terminate the thread (thread1) completely. When I reconnect the Ethernet cable, I click a button on my UI to reestablish comms to the controller, and execute Runthread() Sub. That is when I want to recreate the thread and start it. However, when debugging this part of the code, it appears that the thread (thread1) is never destroyed even though I have verified that the code does get to that line (Case 1) and execute it. After comms is re-established, the code goes right to Case 3 and starts thread1, where I would expect it to jump to Case 2 and recreate the thread.
Public Class frmMain
Private RMC As RMCLink
Public thread1 As System.Threading.Thread = New System.Threading.Thread(AddressOf ReadRegisters)
Private Sub RunThread()
Dim conditions As Integer
If RMC.IsConnected(PingType.Ping) = False And thread1 IsNot Nothing Then
conditions = 1
ElseIf RMC.IsConnected(PingType.Ping) = True And thread1 Is Nothing Then
conditions = 2
ElseIf RMC.IsConnected(PingType.Ping) = True And thread1 IsNot Nothing Then
conditions = 3
End If
Select Case conditions
Case 1
Thread.Sleep(100)
thread1.Abort()
Case 2
Dim thread1 As System.Threading.Thread = New System.Threading.Thread(AddressOf ReadRegisters)
thread1.Start()
Case 3
thread1.Start()
End Select
End Sub
The documentation (https://msdn.microsoft.com/en-us/library/system.threading.thread.abort(v=vs.110).aspx) states that it usually terminates the thread, but as you've found this doesn't always happen.
One way to accomplish this (I'm sure there are others), is to create a public PleaseDie property that your thread's subroutine(s) check periodically, and if that property is set to True, then exit the subroutines within the thread. Before the thread is started, you'll of course have to reset PleaseDie to False.
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);