Insert rows in my listview from a thread - vb.net

I have to create a really simple application in vb. My app contains a button and a listview. In my application i have to run threads, and insert in a listview the name and time of the thread executed. My code starts when i click on the threading button
my code is the following
Private Sub btnThreading_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnThreading.Click
Try
For index = 0 To 3
Dim th As New Threading(index)
Me.Invoke(New DoStuffDelegate(AddressOf th.Run))
Next
lstText.View = View.Details
lstText.Columns.Add("Thread Name")
lstText.Columns.Add("Time")
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
And the page threading is the following
Public Class Threading
Private threadName As String
Dim value As Date
Private Delegate Sub DoStuffDelegate()
Dim lockthis As New Object
Sub New(ByVal ThreadName As String)
Me.threadName = "Thread Number : " & ThreadName
End Sub
Public Sub Run()
For index = 0 To 3
Thread.Sleep(1000)
value = Date.Now
Console.WriteLine(threadName & " Time : " & value)
Dim item3 As New ListViewItem(Me.threadName)
item3.SubItems.Add(Me.value)
Form1.lstText.Items.AddRange(New ListViewItem() {item3})
Next
End Sub
End Class
if I use a thread without the delegate like the following
Dim th As New Threading(index)
Dim trd = New Thread(AddressOf th.Run)
trd.start()
, my output window shows the names and threads as it should for example
Thread Number : 1 Time : 9/15/2014 9:51:00
Thread Number : 0 Time : 9/15/2014 9:51:00
Thread Number : 2 Time : 9/15/2014 9:51:00
Thread Number : 3 Time : 9/15/2014 9:51:00
but doesn't show anything in the list view. but if i choose to use the delegate like the following,
Dim th As New Threading(index)
Me.Invoke(New DoStuffDelegate(AddressOf th.Run))
it shows the following in the output
Thread Number : 1 Time : 9/15/2014 9:50:59
Thread Number : 0 Time : 9/15/2014 9:51:00
Thread Number : 2 Time : 9/15/2014 9:51:01
Thread Number : 3 Time : 9/15/2014 9:51:02
and it shows it in the listview.So it works but doesn't execute the code as a thread does. But finally when I use the following code
Dim th As New Threading(index)
Dim trd = New Thread(AddressOf th.Run)
Me.Invoke(New DoStuffDelegate(AddressOf trd.Start))
, my output window shows the names and threads as it should for example
Thread Number : 1 Time : 9/15/2014 9:51:00
Thread Number : 0 Time : 9/15/2014 9:51:00
Thread Number : 2 Time : 9/15/2014 9:51:00
Thread Number : 3 Time : 9/15/2014 9:51:00
but doesn't show anything in the list view.
I want the list view to show this
Thread Number : 1 Time : 9/15/2014 9:51:00
Thread Number : 0 Time : 9/15/2014 9:51:00
Thread Number : 2 Time : 9/15/2014 9:51:00
Thread Number : 3 Time : 9/15/2014 9:51:00
All threads should have the same time, all threads should start at the same time,and they should appear in my list view.
Please help, I've been working on this for like two weeks and I'm not finding any solution!

What are you adding to the ListView? The name and the time, right? So, when you invoke a method on the UI thread, pas the name AND the time. Don't wait until you're on the UI thread to get the time because obviously that's going to be later.

Related

How do I loop through a DataGridView where RowCount / Rows.Count changes

I have a VB.Net Form with a DataGridView where I wish to change between a Detailed View and a General View.
The DataGridView shows the Distance and Estimated Time between places - called the Route Leg Data and is termed the General View
I have implemented a CheckBox to select between the general and detailed view. When the CheckBox is Checked, I am trying to loop through all of the Route Leg entries and insert the Route Steps which is the Detailed information about the Leg entries.
I have tried various loop options: For..Next, For Each...Next, While...End While, and only the first row (Route Leg) gets processed, even if I have 5 more Route Leg entries.
Important: Keep in mind that when the Detail View is selected, the DataGridView row count increments for every new Route Step entry that gets inserted.
I tried to use both dgv.RowCount and dgv.Rows.Count but I keep getting the same result.
I have added some code to show what I am trying to achieve. Any help or guidance will be much appreciated.
'Show / Hide Route Step Data
Private Sub chkShowRouteStep_CheckedChanged(sender As Object, e As EventArgs) Handles chkShowRouteStep.CheckedChanged
Try
If chkShowRouteStep.Checked Then
'Show Route Steps
For i As Integer = 0 To dgvQuote.RowCount - 1
txtRowCount.Text = i
If dgvQuote.Rows(i).Cells(0).Value.ToString <> "" Then
For j As Integer = 1 To 5
i += 1
dgvQuote.Rows.Insert(i, "Step")
'dgvQuote.Rows.Insert(j + i, "Step")
Next
End If
Next
Else
'Hide Route Steps - WORKS GREAT
For i As Integer = dgvQuote.RowCount - 1 To 0 Step -1
If dgvQuote.Rows(i).Cells(0).Value.ToString = "Step" Then
dgvQuote.Rows.RemoveAt(i)
End If
Next
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
My impatience, as well as a refocus, provided me with the answer.
My focus was always on the DataGridView and the feedback from it with regards to the number of rows contained in the control.
However, the execution of a loop took a snapshot of the number of rows at the start of the loop execution and did NOT update the number of rows every time rows were added to the DataGridView.
I took a different approach where I compared my current loop number (i += 1) with the current number of rows in the DataGridView (dgv.Rows.Count - 1) thereby forcing a recheck on the number of rows. This means that any new rows added to the DataGridView will now be counted in.
I added a trigger (True/False) to be set to true if the last row in the DataGridView was reach and the While...End While loop was exited.
'Show / Hide Route Step Data
Private Sub chkShowRouteStep_CheckedChanged(sender As Object, e As EventArgs) Handles chkShowRouteStep.CheckedChanged
Try
Dim lastRow As Boolean = False 'This will get set when we have reached the last row in the DGV
Dim i As Integer = 0
If chkShowRouteStep.Checked Then
'Show Route Steps
While lastRow <> True
txtRowCount.Text = i
If dgvQuote.Rows(i).Cells(0).Value.ToString <> "" Then
For j As Integer = 1 To 2
'i += 1
dgvQuote.Rows.Insert(i + j, "", "Step")
Next
End If
'Check to see if we have reached the last row, set lastRow to TRUE if it is the last row
If i = dgvQuote.Rows.Count - 1 Then
lastRow = True
End If
i += 1
End While
Else
'Hide Route Steps - WORKS GREAT
For x As Integer = dgvQuote.RowCount - 1 To 0 Step -1
If dgvQuote.Rows(x).Cells(1).Value.ToString = "Step" Then
dgvQuote.Rows.RemoveAt(x)
End If
Next
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
The result is as follows and error free:
Before
After
Since the comment in the code says you want to hide route steps, why not do just that? Instead of removing and inserting rows, populate the grid with everything and then use the checkbox to set the rows .Visible property?

For loop for bowling score array

I am trying to create a bowling program that will display the scores given in a multi-line text box. I've manage to get the program giving an output, but when it runs it skips asking for new inputs and just gives 5 0s on seperate lines and nothing else. I'm completely lost, any help is very much appreciated
EDIT: Sorry should have changed errors to reflect the programs changes, it looks like this now. It gives 0's instead of using the value I gave it, but it does ask for each input now.
For gameNumber As Integer = 1 To 5 Step 1
lblEnterScore.Text = "Enter Score for game #" & gameNumber
Dim Testint As Integer ' define an Integer for testing
Try
Testint = CInt(txtScoreInput.Text) ' try to convert whatever they entered to Int
Catch
MessageBox.Show("Entry is not an Integer") ' If you are here then the CInt failed for some reason, send a message
txtScoreInput.SelectAll()
txtScoreInput.Focus()
Exit Sub
End Try
If txtScoreInput.Text.Contains(".") Then
MsgBox("Bowling Score must be a whole number.")
txtScoreInput.SelectAll()
txtScoreInput.Focus()
Exit Sub
End If
If txtScoreInput.Text > MAXIMUM_SCORE Or txtScoreInput.Text < MINIMUM_SCORE Then
MsgBox("Bowling Score must be between 1 and 300.")
txtScoreInput.SelectAll()
txtScoreInput.Focus()
Exit Sub
End If
scoreInput(gameNumber) = CInt(txtScoreInput.Text)
' and store it in the array
' and increment the gamecounter for the next time through the loop
Next
'btnEnterScore.Enabled = False
' place the good score into the multi-line textbox
txtScoreOutputs.Text = gameScore & vbCrLf & txtScoreOutputs.Text
End Sub
If it was me, here's what I would do... Just a suggestion; I also cut out over half of your code and stopped it from throwing exceptions as well... You can put this in a click event or where ever you need it as well. You can modify this as well to take as many as you want from user input as well not just limit them from entering score's. Your user also has the option to get out of that loop when they choose to do so as well, not keeping them inside the loop...
Private ScoreLists As New List(Of Integer) 'Hold your inputted values
Private Const MAXIMUM_SCORE As Integer = 300 'Max score
Private Const MINIMUM_SCORE As Integer = 1 'Min score
Private blnStop As Boolean = False
Try
For gameNumber As Integer = 1 To 5
Dim intScore As Integer = 0
Do Until (intScore >= MINIMUM_SCORE And intScore <= MAXIMUM_SCORE) OrElse blnStop
If Not Integer.TryParse(InputBox("Please enter a score for game # " & gameNumber.ToString), intScore) Then
If MsgBox("Bowling Score must be a whole number. Stop getting scores?.", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
blnStop = True
Exit For
End If
End If
Loop
ScoreLists.Add(intScore)
Next
'Display the results...
For i As Integer = 0 To ScoreLists.Count - 1
txtScoreOutputs.Text &= ScoreLists.Item(i.ToString)
Next
ScoreLists.Clear()
Catch ex As Exception
End Try
Construct your logic like this (pseudo):
Loop
{
AskUserForInput()
ProcessUserInput()
}
The loop part will control how many scores the user is prompted to enter. The AskUserForInput() function will prompt the user to type in a new score, and the ProcessUserInput() function will get the value and store it in an array or print it to the screen, etc.
Your current logic is not waiting for any new user input before trying to add to the scores. You are also getting zeros because you're setting the txtScoreOutputs with gameScore, which doesn't look like it's been set to the user's input.

Find all numericupdown in form visual basic

you can search on the form all the NumericUpDown and therefore report the value of all zero?
I would like to do something like a for loop that controls the form and see how many objects of that type are available, then if the user presses a key, all the NumericUpDown must return a value of 0 do not know if you can do this, I ask the 'help of you experts.
Dim count As Integer
count = 0
For i = 0 To Me.GroupBox1.Controls.Count - 1
Dim name As String
name = Me.GroupBox1.Controls(i).GetType().ToString()
If name.Contains("NumericUpDown") Then
count = count + 1
End If
Next
Label1.Text = count.ToString()
The form has a Controls collection. You can loop through that and check the type by calling GetType() [inherited from System.Object]. If you want to handle subtypes of NumericUpDown, you could try casting the control to NumericUpDown while catching the exception you will see if the control is not a NumericUpDown.
Your VB coding skills are probably better than mine. Been a long time since I wrote VB. Here's a rough example. I put this code in a button click event. You can pull the whole solution from my GitHub repository: https://github.com/kc1073/Samples
Dim count As Integer
count = 0
For i = 0 To Me.Controls.Count - 1
Dim name As String
name = Me.Controls(i).GetType().ToString()
If name.Contains("NumericUpDown") Then
count = count + 1
End If
Next
Label1.Text = count.ToString()
KC

Adding And Removing Multi-Thread Producers and Consumers in VB.NET with BlockingCollection

Experts,
This is what happens when you can't find a solution anywhere on the Interwebs, and you just have to hack at it until you get it looking good [enough].
I have a situation where I need to parse objects coming in at high speed, but the parsing takes a relatively long time-far slower than they come in... I can tell how many cores are on a box pretty easily, and then I can carve up a team of worker threads (Tasks) to all divide & conquer! But the problem with ANY multi-threaded application is "How Many Threads?"
There is no hard & fast answer, so don't bother looking. My approach is to make a flexible approach that my main thread can monitor to see if throughput (aggregate work completed in X amount of time) is at maximum for the machine it happens to be running on. Also, that same machine may vary in load, RAM available, etc. over time, so you can't just set it & forget it...
I'm just asking to answer my own question, which SO encourages.
If the parsers are expected to be busy all the time (or nearly so), it makes no sense to have more parser threads than CPU threads that can work on them. Having 10 parser threads when you have only 4 cores is pointless because there will be overhead in thread context switches. So allocate 3 or 4 worker threads (consumers) that service the queue.
See https://stackoverflow.com/a/2670568/56778 to determine the number of logical processors that are on your system. It makes no sense to have more worker threads than that. Whether it makes sense to use your complicated scheme of dynamically allocating workers is ... a matter of opinion. I'd be more inclined to have a timer that examines the queue state (number of items) once a minute, and have it allocate or deallocate worker threads appropriately, adding or deleting one worker per minute in order to avoid overshooting the mark. That would probably make very good use of avaliable resources.
Removing a worker would be very easy. If you create an AutoResetEvent that each thread checks each time through its loop, then the first thread that sees it can exit. For example:
private AutoResetEvent _killAThread = new AutoResetEvent(false);
// in each thread
while (!_killAThread.Wait(0) && !cancelToken.IsCancellationRequested)
{
// thread waits for an item from the queue and processes it.
}
Granted, this could cause you to have too many threads waiting if the queue is empty for a long period, but I get the sense that you don't expect that condition to occur very often (if at all).
And, of course, adding a new consumer is easy enough. Your timer tick handler compares the existing queue size against some threshold and spins up a new thread if required. Or, if there are too many threads it calls _killATheread.Set(), and the next thread to finish its processing will check the event, see that it's set, and exit.
[Note: Line numbers refer to those inserted with Crayon for Wordpress at original blog post here. At that link, you can also download a 7z of the entire VS 2012 solution.]
So this is the idea:
So let's see how I solved the issue of the blocking collection and then dishing our work to the Parser Tasks... I used a very generic, and easily re-usable approach of Producer and Consumer. In fact, in my example below, you can even adjust the amount of Producers as well. You can adjust my code below to more closely approximate your work, and then adjust the # of threads/tasks/Consumers to see how well parallelism can work for you.
First, the usual suspects in Imports..
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Collections.Concurrent
Module modStartHere
'
' Producer
'
Dim itemsToProduce = 10
Dim sleepProducer = 10 ' in milliseconds
Dim producerStartID = 1
Dim producersNumToStart = 1
Dim ProducerCTSs As New ConcurrentBag(Of CancellationTokenSource)
Dim moreItemsToAdd As Boolean = True
'
' Consumer
'
Dim sleepConsumer = 1000 ' in milliseconds
Dim consumerStartID = 100
Dim consumersNumToStart = 3
Dim ConsumerCTSs As New ConcurrentBag(Of CancellationTokenSource)
The Producer(s) are initially set up with the above. While the itemsToProduce doesn't change during the program, the number of Producers & Consumers will. This is very rough as a draft, and it will no doubt be streamlined in your own code at some point, but this demonstrates how to solve this problem very well.
I used "IDs" to show in the output which thread was doing what. They only thing needed in production is the list of CTS instances:
'
' the multi-thread-safe queue that is produced to and consumed from
'
Dim bc As New BlockingCollection(Of Integer)
'
' this # will be what is actually produced & consumed (1, 2, 3, ...)
'
Dim itemId As Integer = 0
'
The main machine here is that one little line:
Dim bc As New BlockingCollection(Of Integer)
Microsoft says:
BlockingCollection Overview .NET Framework 4.5
BlockingCollection(Of T) is a thread-safe collection class that
provides the following features:
An implementation of the Producer-Consumer pattern.
Concurrent adding and taking of items from multiple threads.
Optional maximum capacity.
Insertion and removal operations that block when collection is empty or full.
Insertion and removal "try" operations that do not block or that block up to a specified period of time.
Encapsulates any collection type that implements IProducerConsumerCollection(Of T)
Cancellation with cancellation tokens.
Two kinds of enumeration with foreach (For Each in Visual Basic):
-Read-only enumeration.
-Enumeration that removes items as they are enumerated.
itemId is just a variable that holds the fake payload. Producers will increment it by one to simulate a different object instance, or unit of work. You just change the type the BlockingCollection holds...
Now I'm not doing this in a FIFO way (which I will in production), but you can either do that, or even a FILO, as per Microsoft:
When you create a BlockingCollection(Of T) object, you can specify not
only the bounded capacity but also the type of collection to use. For
example, you could specify a ConcurrentQueue(Of T) object for first
in, first out (FIFO) behavior, or a ConcurrentStack(Of T) object for
last in, first out (LIFO) behavior.
Now THAT is useful! Here in this demo, I did it all willy-nilly... But like I said, for my specific need, I need FIFO, as in the diagram at the top...
Later, you will see the functions and subroutines, but the real magic here is in 2 collections-one for Producers and one for Consumers:
Dim ProducerCTSs As New ConcurrentBag(Of CancellationTokenSource)
Dim ConsumerCTSs As New ConcurrentBag(Of CancellationTokenSource)
The Magic: As each Task(thread) is either created or closed, the corresponding CancellationTokenSource is either added or removed from the appropriate collection above.
Seriously, that is it! :)
Next in the code, the initial producers and consumers are created:
'===============================
'
' start demo
'
Sub Main()
'
'===============================
'
' initial state:
'
' start our producer(s)
'
For ps As Integer = producerStartID To producerStartID + producersNumToStart - 1
CreateTask(ps, "Producer")
Next
'
' start our consumer(s)
'
For cs As Integer = consumerStartID To consumerStartID + consumersNumToStart - 1
CreateTask(cs, "Consumer")
Next
'
'=========================================
Aside from a few Thread.Sleep() calls, all the next part does is add or remove producer and consumer Tasks(threads). You can vary the initial values at the top to put it through the paces.
To create a Task... - CreateTask(, <"Producer" or "Consumer">)
To remove a Task, you (in one line) both get a random CTS, and then .Cancel() it:
GetRandomCTS(ProducerCTSs).Cancel()
GetRandomCTS(ConsumerCTSs).Cancel()
GetRandomCTS() takes the collection of CTS instances, picks one at random, then calls Cancel() on it.
'
Thread.Sleep(2000)
'
' create a producer
'
Console.WriteLine("creating producer 555...")
CreateTask(555, "Producer")
Thread.Sleep(1000)
'
' cancel a consumer
'
Console.WriteLine("cancelling random consumer...")
GetRandomCTS(ConsumerCTSs).Cancel()
Thread.Sleep(2000)
'
' cancel a consumer
'
Console.WriteLine("cancelling random consumer...")
GetRandomCTS(ConsumerCTSs).Cancel()
Thread.Sleep(1000)
'
' create a consumer
'
Console.WriteLine("creating consumer 222...")
CreateTask(222, "consumer")
Thread.Sleep(1000)
'
' cancel a producer
'
Console.WriteLine("cancelling random producer...")
GetRandomCTS(ProducerCTSs).Cancel()
Thread.Sleep(1000)
'
' cancel a consumer
'
Console.WriteLine("cancelling random consumer...")
GetRandomCTS(ConsumerCTSs).Cancel()
'
'==========================================
'
Console.ReadLine()
End Sub
And that is it!
Now for the fun parts:
#Region "Utilites"
''' <summary>
''' Retrieves a random cancellation token source from the given list of current threads...
''' Works for either producer or consumer
''' </summary>
''' <param name="ctsBag">ConcurrentBag(Of CancellationTokenSource)</param>
''' <returns>CancellationTokenSource</returns>
''' <remarks></remarks>
Function GetRandomCTS(ctsBag As ConcurrentBag(Of CancellationTokenSource)) As CancellationTokenSource
Dim cts As CancellationTokenSource = Nothing
Dim rndNum As Random = Nothing
Dim rndIndex As Integer = Nothing
Try
If ctsBag.Count = 1 Then
Console.WriteLine("There are no threads to cancel!")
Else
rndNum = New Random(12345)
rndIndex = rndNum.Next(0, ctsBag.Count - 1) ' because ElementAt() is zero-based index
cts = ctsBag.ElementAt(rndIndex)
End If
Catch ex As Exception
Console.WriteLine("GetRandomCTS() Exception: " & ex.StackTrace)
End Try
Return cts
End Function
Line 7: This is what we'll be returning, a CancellationTokenSource
Line 16: ctsBag.ElementAt() allows us to pull out a specific CTS instance by number.
Below, the CreateTask takes an argument for the # you want it to display when running (just for demo, to see which thread is doing what), and a string to tell it whether you want a new Producer of Consumer. Sure, I could have made it more complex, but this is just a rough draft. :)
Private Function CreateTask(taskId As Integer, taskType As String) As CancellationTokenSource
Dim t As Task = Nothing
Dim cts As New CancellationTokenSource()
Dim token As CancellationToken = cts.Token
Try
If taskType.ToLower = "producer" Then
t = Task.Factory.StartNew(Sub() Producer(taskId, token), token, TaskCreationOptions.LongRunning)
ProducerCTSs.Add(cts)
ElseIf taskType.ToLower = "consumer" Then
t = Task.Factory.StartNew(Sub() Consumer(taskId, token), token, TaskCreationOptions.LongRunning)
ConsumerCTSs.Add(cts)
Else
End If
Console.WriteLine("{0} Task {1} ({2}) running!", taskType, taskId.ToString("000"), t.Id)
Catch ex As Exception
Console.WriteLine("Task {0} CreateTask({1}) Exception: ", taskId.ToString("000"), taskType & ex.StackTrace)
End Try
Return cts
End Function
#End Region
Line 7 & 10: These call the Producer() or Consumer() classes below, passing them the CancellationTokenSource needed to let them be able to elegantly be cancelled while running without corrupting any data.
t = Task.Factory.StartNew(Sub() Producer(taskId, token), token, TaskCreationOptions.LongRunning)
Did you notice TaskCreationOptions.LongRunning? That is good in my case, and it improves performance by telling the program to not worry so much about wating what happens for cancellations too closely.
So what does a Producer() class look like?
#Region "Producer(s)"
Public Sub Producer(ByVal taskNum As Integer, ByVal ct As CancellationToken)
' Was cancellation already requested?
If ct.IsCancellationRequested = True Then
Console.WriteLine("Producer Task {0} was cancelled before Producer thread created!", taskNum.ToString("000"))
ct.ThrowIfCancellationRequested()
End If
'
'Dim r As Random = New Random(123)
Dim sw As New Stopwatch
Dim numAdded As Integer = 0
sw.Start()
While moreItemsToAdd = True
' Dim itemIn As Integer = r.Next(1, 1000)
itemId += 1 ' the payload
Try
bc.Add(itemId)
Console.WriteLine("--> " & taskNum.ToString("000") & " --> [+1 => Q has: " & bc.Count & "] added: " & itemId)
numAdded += 1
If ct.IsCancellationRequested Then
Console.WriteLine("Producer Task {0} cancelled", taskNum.ToString("000"))
ct.ThrowIfCancellationRequested()
End If
Thread.Sleep(sleepProducer)
Catch ex As OperationCanceledException
Console.WriteLine("Task " & taskNum.ToString("000") & " cancelling by request!")
Exit While
Catch ex As Exception
Console.WriteLine("Producer() Exception: " & ex.StackTrace)
End Try
If bc.Count >= itemsToProduce Then
moreItemsToAdd = False
End If
End While
sw.Stop()
' Let consumer know we are done.
Console.WriteLine("Producer stopped adding items! Added " & numAdded & " items in " & sw.Elapsed.TotalSeconds & " seconds!")
bc.CompleteAdding()
End Sub
#End Region
I know, I know... it looks complicated! But really it isn't. I'm not that smart! 1/2 the code is just to catch & handle the cancellation requests so that the processing doesn't corrupt any data. That, and a cheesy StopWatch() to time things... And yes, there are artifacts of earlier versions still commented out. Like I said "ROUGH"...
Line 17: Simply adds the itemId (our payload, could be anything) to the BlockingCollection (bc).
Line 20: If a cancellation, we take care of it here, and not a random part of the function, which would likely corrupt all kinds of things...
Line 31: I added this as a cheesy way to tell the Producers when to stop ...producing. This variable (limit) is set at the top of the code.
Line 38: bc.CompleteAdding() - This is a signal to everyone using the bc (BlockingCollection) that there will be no more items added. This way, the consumers know when to stop ...consuming!
"Why would they want to do that?"
Well, suppose you wanted a short-running task, or tasks, and needed to know they were done in order to continue... Yes, in my case, they are long-running, and in production I'll be starting each task with "TaskCreationOptions.LongRunning"
The Consumer() class is almost identical, with just a few tiny differences:
#Region "Consumer(s)"
Public Sub Consumer(ByVal taskNum As Integer, ByVal ct As CancellationToken)
If ct.IsCancellationRequested = True Then ' Was cancellation already requested?
Console.WriteLine("Consumer Task {0} was cancelled before Consumer thread created!", taskNum.ToString("000"))
ct.ThrowIfCancellationRequested()
End If
Dim totalTaken As Integer = 0
Dim sw As New Stopwatch
sw.Start()
While bc.IsCompleted = False
Dim itemOut As Integer = Nothing ' the payload
Try
itemOut = bc.Take()
Console.WriteLine("<-- " & taskNum.ToString("000") & " <-- [-1 => Q has: " & bc.Count & "] took: " & itemOut)
If ct.IsCancellationRequested Then
Console.WriteLine("Consumer Task {0} cancelled", taskNum.ToString("000"))
ct.ThrowIfCancellationRequested()
End If
totalTaken += 1
Catch ex As OperationCanceledException
Console.WriteLine("Task " & taskNum.ToString("000") & " cancelling by request!")
Exit While
Catch e As InvalidOperationException
' IOE means that Take() was called on a completed collection.
' In this example, we can simply catch the exception since the
' loop will break on the next iteration.
End Try
If (Not itemOut = Nothing) Then
Thread.Sleep(sleepConsumer)
End If
End While
sw.Stop()
If bc.IsCompleted = True Then
Console.WriteLine(vbCrLf & "Task " & taskNum.ToString("000") & " - No more items to take. Took " & totalTaken & " items in " & sw.Elapsed.TotalSeconds & " seconds!")
End If
End Sub
#End Region
End Module
Line 3: In both classes, we make sure to check right at the top to see if we've been cancelled. This way, we waste no time or resources if another Task/thread did the last piece of work just as we were being instantiated.
Line 13: itemOut = bc.Take() - Here we grab the next item (depends on FIFO or FILO/LIFO, as configured in the above discussion. This BlockingCollection does it all!
When you sit back and look at it, all the other code in this class is just to dress up Line 13!
So let's fire this puppy up!
Producer Task 001 (1) running!
--> 001 --> [+1 => Q has: 1] added: 1
<-- 100 <-- [-1 => Q has: 0] took: 1
Consumer Task 100 (2) running!
Consumer Task 101 (3) running!
Consumer Task 102 (4) running!
--> 001 --> [+1 => Q has: 1] added: 2
--> 001 --> [+1 => Q has: 2] added: 3
--> 001 --> [+1 => Q has: 3] added: 4
--> 001 --> [+1 => Q has: 4] added: 5
--> 001 --> [+1 => Q has: 5] added: 6
--> 001 --> [+1 => Q has: 6] added: 7
--> 001 --> [+1 => Q has: 7] added: 8
--> 001 --> [+1 => Q has: 8] added: 9
--> 001 --> [+1 => Q has: 9] added: 10
--> 001 --> [+1 => Q has: 10] added: 11
Producer stopped adding items! Added 11 items in 0.1631605 seconds!
<-- 101 <-- [-1 => Q has: 9] took: 2
<-- 100 <-- [-1 => Q has: 8] took: 3
<-- 101 <-- [-1 => Q has: 7] took: 4
<-- 102 <-- [-1 => Q has: 6] took: 5
creating producer 555...
Producer Task 555 (5) running!
<-- 100 <-- [-1 => Q has: 5] took: 6
Producer stopped adding items! Added 0 items in 1.09E-05 seconds!
<-- 101 <-- [-1 => Q has: 4] took: 7
<-- 102 <-- [-1 => Q has: 3] took: 8
cancelling random consumer...
<-- 100 <-- [-1 => Q has: 2] took: 9
<-- 101 <-- [-1 => Q has: 1] took: 10
<-- 102 <-- [-1 => Q has: 0] took: 11
Consumer Task 102 cancelled
Task 102 cancelling by request!
Task 102 - No more items to take. Took 2 items in 2.0128301 seconds!
Task 100 - No more items to take. Took 4 items in 4.0183264 seconds!
Task 101 - No more items to take. Took 4 items in 4.0007338 seconds!
cancelling random consumer...
creating consumer 222...
Task 222 - No more items to take. Took 0 items in 2.8E-06 seconds!
consumer Task 222 (6) running!
cancelling random producer...
cancelling random consumer...
Was this the output you expected?
Grab the entire solution 7z'd up for you, at the link below...
Download Solution from HERE!
It took me a while to figure out the whole CancellationToken concept, but now that I'm using it, and the bullet-proof-ness of the BlockingCollection, I'm confident my application can handle hundreds of objects per second without messing anything up.
My production application will read the amount of cores on the host machine, and use that to set the initial number of consumers. I will then adjust up & down, monitoring the time to complete (in an aggregate fashion), thus making the best of the host machine's resources, understanding that the host machine may be doing many other things at the same time as my application.
Thanks everyone!

VB.NET Application Freezed and not running

I have an application that uses a loop to call a new thread with some parameters, the application is great but when it comes to 100,000 records or so the application is not running (in the CPU and memory taskmgr not change, therefore assume that the program is not working)
The action is:
It runs a function that first collects a number (X) and then create a loop from 0 to X, within this loop we create a new Thread and options as indicated IsBackground (True), Priority (Normal), and initialize the thread.
In the same function Thread.Join have a Do While (true) and if you can cross it refreshed interface, abort thread, memory clean and DoEvents!
Within the thread use a WebClient to OpenRead and keep all this in a Stream.
With WebClient.IsBusy = False Do While we ensure that the information is downloaded and proceed to make a MatchCollection with Regex.Match.
Using a For Each the match and print results, and ended Exit Do.
Then returns to the function that tells a while back ...
At first I thought it was because of the number of results that were printed in the listbox but I got it and still fails, not when it reaches the 100,000 records if not the 100,800 or so.
I need help, I don't know very well programming in vb.net and not understand why.
EDIT:
I tried remove the For Each of thread, check whether it was.
It has emerged completely, but I do not understand why it happens not even give me an error when "For Each" in the thread ¿solutions?
EDIT:
I found the bug, but i don't understand i explain:
A) --> Initialize function with For 0 to 4500, i call a thread with parameters and receive 50 results, ( 225000 registers <- 4500 * 50 ) when the principal For arrive to ~ 4000, in MatchCollection in Thread ¿fails? i don't receive the info of Regex.Matches and then freezed.
B) --> Initialize function with For 4000 to 4500, i call a thread with parameters and receive 50 results, ( 225000 registers <- 4500 * 50 ), nothing more begin the first thread ( remember now begin with 4000 to 4500 and i have receive a 25000 registers ) the program is freezed and i check with break points and fails the variable containing the results of Regex.Matches and attempting looping the that array the program are freezed!
¿Why? ¿Solutions?
Thanks!!
Function 1 (Inicialize Thread)
Function 1 ->
For Me.d = 0 To Pages(0) ' <-- 4500
Dim param(1) As Object
param(0) = "name"
param(1) = "url/" & d
Dim thread As New Thread(AddressOf Thread_)
thread.IsBackground = True
thread.Name = "name"
thread.Priority = ThreadPriority.Normal 'ThreadPriority.Lowest
thread.Start(param)
Do While thread.Join(True)
thread = Nothing
ClearMemory()
Application.DoEvents()
Exit Do
Loop
Next d
The Thread function:
Public Sub Thread_(ByVal param As Object)
Try
If param(0) = "name" Then
Using Client As New WebClient
Client.Headers("User-Agent") = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.13 (KHTML, like Gecko) Chrome/24.0.1290.1 Safari/537.13"
Dim html As Stream = Client.OpenRead(param(1))
Using reader As New StreamReader(html)
Do While wc.IsBusy = False
Dim ReadStr = reader.ReadToEnd
links = Regex.Matches(ReadStr, "coderegex", RegexOptions.Compiled + RegexOptions.RightToLeft + RegexOptions.Multiline + RegexOptions.Singleline)
For Each match In links
Registres(0) = Registres(0) + 1
Debug.Print("Registro: " & Registres(0) & " pág:" & PagesComplete(0))
Next
PagesComplete(0) = PagesComplete(0) + 1
links = Nothing
Client.Dispose()
Exit Do
Loop
End Using
End Using
End If
Catch ex As Exception
Debug.Print("Error " & ex.Message)
End Try
End Sub
This part looks suspicous:
Do While thread.Join(True)
thread = Nothing
ClearMemory()
Application.DoEvents()
Exit Do
Loop
shouldn't that look like this:
thread.Join()
thread = Nothing
ClearMemory()
If you're using framework >=4 then you might consider using Parallel.For (MSDN). Instead of creating your own threads you let the framework handle it for you.