Question Adding Items From 1 RichTextBox into Different Arrays

digitaldrew

Well-known member
Joined
Nov 10, 2012
Messages
167
Programming Experience
Beginner
I've been struggling to figure out how to do this and hope someone can help. Here is my scenario...

I have a RichTextBox with multiple items inside and the number of items may vary from one day to the next. I also have the option to run my program on either 1, 2 or 3 different threads (each thread will have its own array to work from). Once started, I would like the program to look at how many items are listed in the RichTextBox and then split them up evenly into each individual array.

For example, if I put 1 item in the RichTextBox and select 1 thread, it will simply add that one item into the array created for the first thread. If I put 3 items into the RichTextBox and select 3 threads, it will take the first item and add it to the first array, the second item and add it to the second array, and then add the third item to the third array. If I put 6 items in and select 3 threads, it will put the first two into the first array, the second two into the second array, and the final two into the third array. Keep in mind, there might be days where I have 5 items in the RichTextBox and 2 threads selected. If this were the case then I would want the first 2 items added to the first array and the remaining 3 added to the second array.

Here is some code I've been messing around with, but I know I'm way off because I've never done this before. You can tell by the very first line that if I don't have a round number it's not going to work. For example, if I were to put 7 items into the RichTextBox and select 3 threads, the Integer I'm going to get is 2.3333333

VB.NET:
'Get number of items in RichTextBox and divide by number of threads selected to determine how many items should go into each individual array
Dim itemsPerThread As Integer = txtItems.Lines.Count / nmThreads.Value

'Run loop to add the itemsPerThread into arrays
Dim myitemArray1 As New ArrayList
Dim myitemArray2 As New ArrayList
Dim myitemArray3 As New ArrayList
Dim x As Integer = 1
Dim arrayCount As Integer = 1

For i As Integer = 0 To txtItems.Lines.Count - 1
    While x <= itemsPerThread
        myitemArray(arrayCount).Add(i)
        x = x + 1
    End While
    x = 0
    arrayCount = arrayCount + 1
Next
 
Firstly, don't keep using the Lines property over and over. Each time you get that property, a new array is created. If you need to use the data multiple times then get the property once, assign the value to a variable and use that variable multiple times.

Secondly, don't use an ArrayList for anything unless you live before 2005. Either use an array or, if you need dynamic resizing, use a List(Of T).

As for the issue, there are various options but here's one way to solve it:
VB.NET:
Private Function GetThreadData(data As String(), threadCount As Integer) As String()()
    'The number of items left after evenly dividing the data.
    Dim remainder As Integer

    'The number of items per thread after evenly dividing the data.
    Dim itemsPerThread = Math.DivRem(data.Length, threadCount, remainder)

    Dim threadData As New List(Of String())

    'The number of items already taken.
    Dim skipCount As Integer = 0

    For i = 0 To threadCount
        Dim takeCount = itemsPerThread

        If remainder > 0 Then
            'Add an extra item to this partition.
            takeCount += 1
            remainder -= 1
        End If

        threadData.Add(data.Skip(skipCount).Take(takeCount).ToArray())
        skipCount += takeCount
    Next

    Return threadData.ToArray()
End Function
Then you would do this:
VB.NET:
Dim threadData = GetThreadData(txtItems.Lines, CInt(nmThreads.Value))
You now have an array that contains an input array for each thread.

Having said all that, forget all that. Creating input dedicated to each thread is a bad idea. What if one thread finishes its data while another has multiple items left to process? You're actually wasting resources. It would actually be better to use a single ConcurrentQueue and let each thread just take the next available item when it wants to. Unlike a regular Queue, the ConcurrentQueue is guaranteed to work with multiple threads and you also guarantee that each thread is utilised to its maximum degree without wasting time partitioning the data. Try something like this:
VB.NET:
Private data As ConcurrentQueue(Of String)

Private Sub ProcessItems()
    While ProcessItem()
    End While
End Sub

Private Function ProcessItem() As Boolean
    Dim item As String
    Dim result = data.TryDequeue(item)

    If result Then
        Console.WriteLine("Item '{0}' processed on thread {1}", item, Thread.CurrentThread.ManagedThreadId)
    End If

    Return result
End Function
and:
VB.NET:
data = New ConcurrentQueue(Of String)(txtItems.Lines)

For i = 1 To CInt(nmThreads.Value)
    Call New Thread(AddressOf ProcessItems).Start()
Next
 
Keep in mind, there might be days where I have 5 items in the RichTextBox and 2 threads selected. If this were the case then I would want the first 2 items added to the first array and the remaining 3 added to the second array.
Just a note about this distribution. If you have 5 items and 3 threads this will result in a (1,1,3) set. It would be better to round the size up and let last thread take remainder, which would result in a (2,2,1) set. Using Linq methods Skip and Take would be helpful in writing such a Split method.

Anyway, the idea about using a queue is better.
 
Thanks for the replies everyone! I'm trying to setup and use a queue like jmcilhinney recommended. Will update how it goes!
 
Back
Top