Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Equivalent of do() while{} in Parallel

How can I create the parallel equivalent of a do-while or similar in the Update() method below?

Another thread in the app writes to TestBuffer at random. TestBuffer.RemoveItemAndDoSomethingWithIt(); should run until the TestBuffer is empty. Currently Update() only runs with the items that were in the collection when it was was enumerated, which makes sense.

internal class UnOrderedBuffer<T> where T : class
{
    ConcurrentBag<T> GenericBag = new ConcurrentBag<T>();
}

internal class Tester
{
    private UnOrderedBuffer<Data> TestBuffer;

    public void Update()
    {
        Parallel.ForEach(TestBuffer, Item =>
        {
            TestBuffer.RemoveItemAndDoSomethingWithIt();
        });
    }
}
like image 219
Canacourse Avatar asked Sep 13 '11 10:09

Canacourse


3 Answers

You could force a single execution by 'prepending' a null/default value:

static IEnumerable<T> YieldOneDefault<T>(this IEnumerable<T> values)
{
    yield return default(T);
    foreach(var item in values)
        yield return item;
}

And then use it as follows:

Parallel.ForEach(TestBuffer.YieldOneDefault(), Item =>  
{  
    if(Item != null)
      TestBuffer.RemoveItemAndDoSomethingWithIt();
    else
      DoSomethingDuringTheFirstPass();
});  

Although I suspect you might be looking for the following extension methods:

public static IEnumerable<IEnumerable<T>> GetParrallelConsumingEnumerable<T>(this IProducerConsumerCollection<T> collection)
{
    T item;
    while (collection.TryTake(out item))
    {
        yield return GetParrallelConsumingEnumerableInner(collection, item);
    }
}

private static IEnumerable<T> GetParrallelConsumingEnumerableInner<T>(IProducerConsumerCollection<T> collection, T item)
{
    yield return item;
    while (collection.TryTake(out item))
    {
        yield return item;
    }
}

Which will let get you this result (which I think is what you are after):

Parallel.ForEach(TestBuffer.GetParrallelConsumingEnumerable(), Items =>       
{
    foreach(var item in Items)
    {
       DoSomethingWithItem(item);
    }
});
like image 136
Jonathan Dickinson Avatar answered Sep 20 '22 11:09

Jonathan Dickinson


for/foreach are usually used for performing a task on multiple items.

while-do/do-while are for:

a. Performing a task on multiple items that have not yet been enumerated (e.g. a tree).
- In this case you can define a BFS or DFS enumerator and use in a foreach.

b. Performing iterative work on a single item
- Iterative work is not suitable for parallelism

Do not attempt to refactor code from serial to parallel. Instead, consider what your assignment is and how it is best done in parallel. (Refactor algorithm, not code.)

like image 41
Danny Varod Avatar answered Sep 23 '22 11:09

Danny Varod


public static void While( 
    ParallelOptions parallelOptions, Func<bool> condition, 
    Action<ParallelLoopState> body) 
{ 
    Parallel.ForEach(Infinite(), parallelOptions, (ignored, loopState) => 
    { 
        if (condition()) body(loopState); 
        else loopState.Stop(); 
    }); 
}

private static IEnumerable<bool> Infinite() 
{ 
    while (true) yield return true; 
}
like image 30
Sayed Javad Sayedi Avatar answered Sep 21 '22 11:09

Sayed Javad Sayedi