Click here to Skip to main content
15,888,984 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a program that starts with a list of objects of a specific class. The first object is assigned to a local variable and then removed from the list. Work is performed on this local variable which may result in a new list of objects of the same class. if a list is returned then the objects are inserted back into the original list sorted based on a property of the class. The process is repeated until there are no more objects in the list.

The code is based on a branch and bound approach to finding the optimal solution to a problem. Each answer receives a score which is the Result property. The lower the score the better. The answers with the best score are chosen to be iterated on producing new answers, each with their own score, which are placed back into the list. Once certain criteria are met, an answer is considered final and is no longer worked on. Once an answer is final it is checked against the current best answer (if any) and becomes the best answer if it has a better score. The way the scoring algorithm work makes it impossible for an answer to produce an answer with a lower score, thus once a best answer is found any answers in the list with a higher score cannot produce a better answer and can be removed from the list.

VB
Dim MainList as new List(of B)
Populate(MainList ) ' Populates MainList 
Dim Best as B = Nothing

Do
  Dim Current as B = MainList(0)
  MainList.RemoveAt(0)
  Dim SubList as list(of B)=PerformWork(Current) ' Function that preforms the work
  if Sublist.Count > 0 then
    If Sublist(0).IsFinal then ' Note that only 1 item will be returned if IsFinal is true
      If Best Is Nothing OrElse Best.Result > SubList(0).Result then
        Best=Sublist(0)
        Remove(Best.Result) ' Removes all items in MainList that have a Result > Best.Result
      End if
    Else
      Insert(SubList) ' Sub that inserts all the items in SubList into MainList
    End if
  end if
Loop While MainList.Count > 0


My goal is to parallelize this process by taking the first n number of objects and starting a thread for each which will return the list of objects. As each thread returns, the returned list of objects will be inserted into the list and the current first object will be passed to a new thread.

Every example I have seen will just loop through the entire list and create a new thread for each object. Since I only want to focus on the best objects and this will change as the program runs, I do not think this is the best approach. I could just loop through the first 4 items, and keep that going, but I plan on using multiple computers and each would have to have the software tuned specifically for it. Is there any way to have the framework determine the best number of threads?

What I have tried:

Nothing code wise so far, just looked at a lot of articles that are not much help.
Posted
Updated 14-Jul-17 8:29am
v2

1 solution

I don't think the sorting makes any sense. If you're processing the list in parallel, the items can be processed in any order.

The List(Of T) class is not a good choice. It's not thread-safe, and removing items from the start of the list is an expensive operation. I'd suggest using the ConcurrentQueue(Of T)[^] class instead.

You'll need an extension method to get a "consuming" enumerator, that removes items from the queue as they're processed:
VB.NET
Module CollectionExtensions
    <Extension()> 
    Public Iterator Function GetConsumingEnumerator(Of T)(ByVal source As IProducerConsumerCollection(Of T)) As IEnumerable(Of T)
        Dim item As T
        While source.TryTake(item)
            Yield item
        End While
    End Function
End Module

You can then use the AsParallel[^] extension method to consume the list in parallel:
VB.NET
Dim best As B = Nothing
Dim initialList As New List(Of B)
Populate(initialList)

Dim mainList As New ConcurrentQueue(Of B)(initialList)
While Not mainList.IsEmpty
    Dim newItems As IEnumerable(Of List(Of B)) = mainList _
        .GetConsumingEnumerator() _
        .AsParallel() _
        .Where(Function (current) best Is Nothing OrElse current.Result <= best.Result) _
        .Select(Function (current) PerformWork(current)) _
        .Where(Function (newList) newList.Count > 0)
        
    For Each newList As List(Of B) In newItems
        If newList(0).IsFinal Then
            If best Is Nothing OrElse best.Result > newList(0).Result Then
                best = newList(0)
            End If
        Else
            For Each item As B In newList
                mainList.Enqueue(item)
            Next
        End If
    Next
End While

NB: You have a mistake in your current code:
If Best IsNot Nothing OrElse Best.Result > SubList(0).Result then
If Best is Nothing, you'll get a NullReferenceException on that line.
 
Share this answer
 
Comments
Beachhouse13 14-Jul-17 22:48pm    
Richard, Thank you for your reply, but that will not work for me. I have to apologize as my original post was not that clear on my intent. I have added more description to help with that. Essentially, I do not want to work on every item in the list, which is why the sorting is important. With that I cannot use the ConcrurrentQueue will not work as I cannot insert items into it. I realize that the List class is slow and that is next on my list to make more efficient, but for now that is the best I can do. I plan on being able to use the list as I only access it on the main thread and not in any of the worker threads.

Also, thank you for pointing out the error in the code. That has been fixed.
Richard Deeming 17-Jul-17 6:53am    
If you don't know which items you want to work on until you've evaluated item #0, then you can't work on the items in parallel.
Beachhouse13 18-Jul-17 13:15pm    
I don't know which is the best item to work on, but I can go ahead and work on the best 3, 4, or however many the CPU can handle at once. It may lead to a situation where an item is evaluated when it wouldn't have if I had gone sequentially, but that would be rare and the speedup from running in parallel would be more beneficial.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900