7

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();
        });
    }
}
CarenRose
  • 1,266
  • 1
  • 12
  • 24
Canacourse
  • 1,805
  • 2
  • 20
  • 37

3 Answers3

5

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);
    }
});
CarenRose
  • 1,266
  • 1
  • 12
  • 24
Jonathan Dickinson
  • 9,050
  • 1
  • 37
  • 60
  • As I understand the question the answer the OP is looking for is the first extension method you wrote. – Motti Sep 13 '11 at 10:40
  • @Jonathan Dickinson Tried both your suggestions and got "Error 1 Extension method must be defined in a non-generic static class" on both. – Canacourse Sep 13 '11 at 10:45
  • @Canacourse - what does the error say? Hint: it tells you how to to define the extension methods. – Jonathan Dickinson Sep 13 '11 at 10:47
  • I had mistakenly assumed your code was intended for the UnorderedBuffer class. Thanks for the answer (And hint) – Canacourse Sep 13 '11 at 10:59
5

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.)

CarenRose
  • 1,266
  • 1
  • 12
  • 24
Danny Varod
  • 17,324
  • 5
  • 69
  • 111
2
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; 
}