Simultaneous Threads

andyred

Active member
Joined
Mar 5, 2009
Messages
27
Programming Experience
1-3
Hi Guys. One slight problem here

OK - I have a class that wraps your worker thread and logic to pass over a list of arrays, e.g.

VB.NET:
Public Class ExtractWorker 

    ' Private 
    Private mUrls As List(Of String) 
    Private mWorkerId As String 
    Private mWorkerThread As Threading.Thread 

    Public Sub New(ByVal urlsToProcess As List(Of String), ByVal workerId As String) 
        ' Set properties 
        mUrls = urlsToProcess 
        mWorkerId = workerId 
    End Sub 

    Public Sub StartProcessing() 
        ' Creates a new thread to start the internal processing 
        mWorkerThread = New Threading.Thread(AddressOf ProcessArray) 
        mWorkerThread.Name = "Worker " & mWorkerId 
        mWorkerThread.Start() 
    End Sub 

    Private Sub ProcessArray() 
        ' Runs through the array processing each Url 
        For Each url As String In mUrls 
            ' Output 
            Debug.WriteLine("Worker: " & mWorkerId & " processing url: " & url) 
            ' Do your extraction code here 
            ' Use thread.sleep to emulate processing 
            Threading.Thread.Sleep(5) 
        Next 
    End Sub 

End Class

Then to start the workers, I have a list of dummy URLs and create the worker instances, e.g

VB.NET:
  Dim testUrls As New List(Of String) 
        For n As Integer = 1 To 100 
            testUrls.Add("www.mysite.com/" & n.ToString) 
        Next 

 Dim threadsNo as Integer=15 
 Dim workers As New List(Of ExtractWorker) 
        For n As Integer = 0 To threadsNo 
            Dim ew As New ExtractWorker(testUrls.GetRange(n, testUrls.count / threadsNo), n.ToString()) 
            workers.Add(ew) 
            ew.StartProcessing() 
Next


My problem is: This code creates 15 threads (one thread-one url) taking care of first 15 urls in testUrls. How do I make these threads start again from url 16 and so on until they finish the entire testUrls list (100 items or urls in this case)?

Thanks
 
What would be the best distribution of 100 work items on 15 threads? 100/15=6.6, so 6 or 7 work items each?
15*6=90, 10 remaining items for one additional worker to do.
15*7=105, that's too many, one of the workers can only do 2 work items, 5 less than the others.
Perhaps a combination of 6 or 7 work items each?
8*6 + 7*7=97, 3 remaining items for one additional worker to do.
7*6 + 8*7=98, 2 remaining items for one additional worker to do.
The logic? 100/(6+7)=7.7, which means 7 of one and 8 of the other, ie two combinations possible.

Could there be a better combination?
For 6 work items we can use up to 15 workers and see how the remaining divides by 7, then see how many remaining work items there are with each combination:
1*6=6, 94\7=13, 3 remaining
2*6=12, 88\7=12, 4 remaining
3*6=18, 82\7=11, 5 remaining
4*6=24, 76\7=10, 6 remaining
5*6=30, 70\7=10, 0 remaining
6*6=36, 64\7=9, 1 remaining
7*6=42, 58\7=8, 2 remaining
8*6=48, 52\7=7, 3 remaining
9*6=54, 46\7=6, 4 remaining
10*6=60, 40\7=5, 5 remaining
11*6=66, 34\7=4, 6 remaining
12*6=72, 28\7=4, 0 remaining
13*6=78, 22\7=3, 1 remaining
14*6=84, 16\7=2, 2 remaining
15*6=90, 10\7=1, 3 remaining
As you can see two of these combinations adds up exactly to the number of work items, but this is never guaranteed, using a calculation like this normally leaves one or more remaining items that has to be handled by an additional worker.
This perhaps look more and more complicated, but the math is as basic as you first learnt it, all these algorithms can be implemented with a few variables and loops and the primary math.

While this is interesting, the .Net framework already has built in functionality for distribution and queueing of threads, it is called ThreadPool and it has 20 available threads by default. Using ThreadPool.QueueUserWorkItem you can load as many work items you want and only 20 is processed simultaneously, the others queue up automatically. The ThreadPool also has idle threads, which means there is basically no startup time for new threads and queued items.
 
Can you please show me how my code can be modified to do this properly. I'm not very familiar with threadpool. I really appreciate it. Thanks
 
That's really weird I just looked and I have the same calculations on paper. I have reached the conclusion that the first 14 threads should handle 6 urls each and the last thread should handle 10 urls (the rest up to 100)
The only problem is I read and read and I don't know how to put this in code.
Threadpool sounds good but if you can teach me how to use for my code it it would be great.

Thanks in advance
 
Threadpool sounds good but if you can teach me how to use for my code it it would be great.
Have a look in help, most pages there have code samples relevant to the class or class member explained, here the online help page for ThreadPool class.

Also notice a difference between how you are doing distribution and how the ThreadPool does it. You are dividing up a finite set of work items and assign these to specific workers (and problem as discussed the division rarely adds up to even work items), while Threadpool only accept a single queue of work items that any available worker will dequeue one item from.

Can you please show me how my code can be modified to do this properly.
Specifically, your original code has these three problems:
  • testUrls.count / threadsNo = workitemcount, implicitly converted to Integer this rounds up to 7. So was this intended, or did you mean for it to be 6? In the latter case you should do integer division, ie \ instead of /.
  • For n As Integer = 0 To threadsNo, 0 to 15 means you are runnning 16 workers and not 15 as you have calculated with. 16*7=112 = way off target.
  • GetRange(n,workitemcount), for n loop this will take 7 items starting from index 0, take 7 items starting from index 1, take 7 items starting from index 2, etc. You need to take workitemcount items starting from index n*workitemcount, ie take 7 items from index 0, take 7 items from index 7, take 7 items from index 14, etc.
The you have the calculation about remaining items, once you know that number you can to start another worker either before or after the loop, this is normally easier than including this into the loop.
 
I decided to completely change my code and go for threadpool.
I like the fact Threadpool only accepts a single queue of work items that any available worker will dequeue one item from.
My questions are:

1. How can I set the numer of threads (by the way which is the maximum number I can use, can I use 100?)

2. If I have 100 threads must I define each of them with ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ThreadProc), ti) where ti = new taskinfo?

3. If I have 1000 items to process how are these items shared between the threads?
VB.NET:
For i = 0 To 100 '(1000 \ 10)
        ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ThreadProc), ti(i) )
Next

Should I define one of the above for each thread?

4. After processing all the 1000 items will the threads stop by themselves?
How can I stop them manually?

5. Can I link the progress of the threads to a progressBar?
 
  1. ThreadPool.SetMaxThreads method. Note that the default value is set for a reason, after probably a lot of testing the .Net team has decided this generally gives a better balance between resources used and the need for multiple tasks processing at the same time. Running too many threads simultaneously will both be a memory hog and will slow down the overall processing time as all the threads fight to get a slice of the finite amount of available CPU cycles.
  2. yes, if the work needs an argument you pass this info with the state parameter.
  3. any available thread will take the next work item in queue.
  4. yes. the drawback of ThreadPool is you can't dequeue an item, you can use a "cancel" Boolean variable available to the queued worker methods (declared in class), where you simply skip the work when cancel=True, check this variable before and/or during the work as it may apply.
  5. you can for example use two Integer variables (total, completed), each worker done would add 1 to completed and can then calculate progress percentage and report it to the UI.
 
Ok I have managed to make it work.
I have another problem now, I am using 15 threads maximum to process 5000 items but my main form freezes, the only solution is to press stop debugging.
Can it have something to do with the fact I set ThreadPool.SetMaxThreads(15, 15)?
Any ideas?
 
Last edited:
Default completion threads is 1000, I have no idea what effect changing this could have. UI freeze usually means you are blocking the thread somehow, preventing windows messages to be handled.
 
You can select "Break all" in debug menu, if a call in UI thread is not finished it will point to it.
 
Better yet, here's the entire code:

VB.NET:
Public Sub DoWork()
        Dim isDone As New AutoResetEvent(False)
        ThreadPool.SetMaxThreads(15, 15)
        ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf TaskA), isDone)
        Dim ThreadCount, ThreadPorts As Integer
        Dim workerThreads, completionPortThreads As Integer

        ThreadPool.GetAvailableThreads(ThreadCount, ThreadPorts)
        Console.WriteLine("Thread pool size {0}", ThreadCount)

        ThreadPool.GetMaxThreads(workerThreads, completionPortThreads)
        Console.WriteLine("workerThreads: {0} completionPortThreads: {1}", workerThreads, completionPortThreads)

        Console.WriteLine("Waiting for asynchronous threads to complete.")

        isDone.WaitOne()
        Console.WriteLine("is done")
    End Sub

Public Sub TaskA(ByVal state As Object)
        Dim I As Integer
        For I = 0 To 5000
            System.Console.Write("A")
        Next
        Thread.Sleep(3000)
        CType(state, AutoResetEvent).Set()
    End Sub

DoWork is assigned to a button in my application

Can you please test it and see if your GUI freezes also? I cannot move, minimize or do anything else to the main form of the application while the threadpool is running.
I would really appreciate it. Thanks
 
I'm using VS 2008 and I do not have a Break All in the Debug menu
Yes, you do. It only appears at runtime when it is possible to use it, it's the same as the "pause" button.
Toolstrip:
breakall.png

Menu:
breakall2.png

You can't have isDone.WaitOne() in UI thread, it stops the thread. Break into debug and you see this is the line currently executing (ie blocking) in the UI thread:
breakall3.png
 
I thought WaitOne is used when all threads finish. How can I print a message all threads are finished when they really are finished?
 
Back
Top