Эквивалент do () while{} параллельно


Как получить код параллельного эквивалента do while или аналогичного в методе Update () ниже.

Другой поток в приложении записывает в Testbuffer наугад. TestBuffer.RemoveItemAndDoSomethingWithIt(); должен выполняться до тех пор, пока тестовый буфер не опустеет. В настоящее время Update() выполняется только с элементами, которые были перечислены в коллекции, когда она была. Что вполне логично...

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


namespace Test
{
  internal class Tester
  {
    private UnOrderedBuffer<Data> TestBuffer;

    public void Update()
    {
      Parallel.ForEach(TestBuffer, Item =>
      {
        TestBuffer.RemoveItemAndDoSomethingWithIt();
      });
    }
  }
}
2 3

2 ответа:

Можно принудительно выполнить одно выполнение, добавив перед ним значение null / default:

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

И затем использовать его следующим образом:

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

Хотя я подозреваю, что вы, возможно, ищете следующие методы расширения:

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;
    }
}

Который позволит получить вам этот результат (который, я думаю, является тем, что вы ищете):

Parallel.ForEach(TestBuffer.GetParrallelConsumingEnumerable(), Items =>       
        {
            foreach(var item in Items)
            {
               DoSomethingWithItem(item);
            }
        });
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 you assignment is and how it is best done in parallel.
(Refactor algorithm, not code.)