C# 为什么';t WithMergeOptions(ParallelMergeOptions.NotBuffered)是否立即提供结果?
(我目前仅限于.NET 4.0) 我的情况是,我希望尽可能多地并行处理项目,必须维持订单,并且可以随时添加项目,直到按下“停止” 项目可能以“突发”的形式出现,因此队列可能会完全耗尽,暂停,然后大量项目将再次出现 我希望结果一完成就可以得到 以下是一个简化的示例:C# 为什么';t WithMergeOptions(ParallelMergeOptions.NotBuffered)是否立即提供结果?,c#,.net,task-parallel-library,plinq,C#,.net,Task Parallel Library,Plinq,(我目前仅限于.NET 4.0) 我的情况是,我希望尽可能多地并行处理项目,必须维持订单,并且可以随时添加项目,直到按下“停止” 项目可能以“突发”的形式出现,因此队列可能会完全耗尽,暂停,然后大量项目将再次出现 我希望结果一完成就可以得到 以下是一个简化的示例: class Program { static void Main(string[] args) { BlockingCollection<int> itemsQueue = new Bloc
class Program
{
static void Main(string[] args)
{
BlockingCollection<int> itemsQueue = new BlockingCollection<int>();
Random random = new Random();
var results = itemsQueue
.GetConsumingEnumerable()
.AsParallel()
.AsOrdered()
.WithMergeOptions(ParallelMergeOptions.NotBuffered)
.Select(i =>
{
int work = 0;
Console.WriteLine("Working on " + i);
//simulate work
for (int busy = 0; busy <= 90000000; ++busy) { ++work; };
Console.WriteLine("Finished " + i);
return i;
});
TaskCompletionSource<bool> completion = new TaskCompletionSource<bool>();
Task.Factory.StartNew(() =>
{
foreach (int i in results)
{
Console.WriteLine("Result Available: " + i);
}
completion.SetResult(true);
});
int iterations;
iterations = random.Next(5, 50);
Console.WriteLine("------- iterations: " + iterations + "-------");
for (int i = 1; i <= iterations; ++i)
{
itemsQueue.Add(i);
}
while (true)
{
char c = Console.ReadKey().KeyChar;
if (c == 's')
{
break;
}
else
{
++iterations;
Console.WriteLine("adding: " + iterations);
itemsQueue.Add(iterations);
}
}
itemsQueue.CompleteAdding();
completion.Task.Wait();
Console.WriteLine("Done!");
Console.ReadKey();
itemsQueue.Dispose();
}
}
类程序
{
静态void Main(字符串[]参数)
{
BlockingCollection itemsQueue=新建BlockingCollection();
随机=新随机();
var results=itemsQueue
.getconsumineGenumerable()
.天冬酰胺()
.AsOrdered()
.WithMergeOptions(ParallelMergeOptions.NotBuffered)
.选择(i=>
{
整数功=0;
Console.WriteLine(“处理”+i);
//模拟工作
对于(int busy=0;busy)
{
foreach(结果中的int i)
{
Console.WriteLine(“可用结果:+i”);
}
完成。设置结果(真);
});
整数迭代;
迭代=随机。下一步(5,50);
Console.WriteLine(“----iterations:+iterations+”----”;
对于(int i=1;i请注意,如果可以调用BlockingQueue.CompleteAdding()
instance方法,则问题不是问题-这将导致所有结果完成
简短回答
另一方面,如果您需要维持秩序,并且需要尽快获得结果,并且您没有机会调用BlockingQueue.completedadding()
,那么如果可能的话,最好让队列中的项目的消耗是非并行的,但要并行处理每个任务
例如
替代方法
如果将单个任务并行化(如“简短回答”中的建议)如果不可能,并且所有其他问题约束都适用,那么您可以实现您自己的队列类型,为每个项目启动任务-从而让任务并行库处理工作调度,但自行同步结果的使用
例如,类似下面的内容(带有标准的“无担保”免责声明!)
公共类QueuedItem
{
私有只读对象_lockObject=新对象();
私有结果;
专用只读输入;
私有只读TResult\u notfinished;
内部只读bool IsEndQueue=false;
内部QueuedItem()
{
IsEndQueue=true;
}
公共QueuedItem(TInput输入,TResult未完成)
{
_输入=输入;
_未完成=未完成;
_结果=_未完成;
}
公共TResult ReadResult()
{
锁定(锁定对象)
{
如果(!IsResultReady)
抛出新的InvalidOperationException(“在调用ReadResult()之前检查IsResultReady”);
返回结果;
}
}
public void WriteResult(TResult值)
{
锁定(锁定对象)
{
如果(IsResultReady)
抛出新的InvalidOperationException(“结果已写入”);
_结果=值;
}
}
公共TInput输入{get{return}
公共图书馆已准备就绪
{
得到
{
锁定(锁定对象)
{
return!object.Equals(_result,_notfinished)| IsEndQueue;
}
}
}
}
公共类ParallelImmediateOrderedProcessingQueue
{
私有只读ReaderWriterLockSlim _addLock=new ReaderWriterLockSlim();
私有只读对象_readingResultsLock=new object();
私有只读ConcurrentQueue_ConcurrentQueue=新ConcurrentQueue();
bool_isfinishedding=false;
私有只读TResult\u notFinished;
专用只读操作处理器;
///指示结果尚未完成的值
///完成后必须对参数调用SetResult()。
public ParallelImmediateOrderedProcessingQueue(TResult notFinished,Action processor)
{
_未完成=未完成;
_处理器=处理器;
}
公共事件操作ResultsReady=委托{};
私有void SignalResult()
{
队列数据项;
if(_concurrentQueue.TryPeek(out项)和&item.IsResultReady)
{
结果就绪();
}
}
公共作废添加(TInput输入)
{
bool shouldtrow=false;
_addLock.EnterReadLock();
{
shouldThrow=_isfinished dadding;
如果(!shouldThrow)
{
var queuedItem=新的queuedItem(输入,_notFinished);
_concurrentQueue.Enqueue(queuedItem);
Task.Factory.StartNew(()=>{u处理器(queuedItem);SignalResult();});
}
}
_addLock.exitradlock();
如果(应该扔)
抛出新的InvalidOperationException(“尝试添加项目,但添加项目被标记为已完成”);
}
公共IEnumerable ConsumerReadyResults()
{
//保持秩序所需的锁
锁定(_readingResultsLock)
{
QueuedItem QueuedItem;
while(_concurrentQueue.TryPeek(out-queuedItem)和&queuedItem.IsResultReady)
{
if(!\u concurrentQueue.TryDequeue(out-queuedItem))
抛出新的ApplicationException(“这不应该发生”);
if(queuedItem.IsEndQueue)
{
_完成。设置结果(真);
}
其他的
{
排队收益率
class Program
{
//Not parallel, but suitable for monitoring queue purposes,
//can then focus on parallelizing each individual task
static void Main(string[] args)
{
BlockingCollection<int> itemsQueue = new BlockingCollection<int>();
Random random = new Random();
var results = itemsQueue.GetConsumingEnumerable()
.Select(i =>
{
Console.WriteLine("Working on " + i);
//Focus your parallelization efforts on the work of
//the individual task
//E.g, simulated:
double work = Enumerable.Range(0, 90000000 - (10 * (i % 3)))
.AsParallel()
.Select(w => w + 1)
.Average();
Console.WriteLine("Finished " + i);
return i;
});
TaskCompletionSource<bool> completion = new TaskCompletionSource<bool>();
Task.Factory.StartNew(() =>
{
foreach (int i in results)
{
Console.WriteLine("Result Available: " + i);
}
completion.SetResult(true);
});
int iterations;
iterations = random.Next(5, 50);
Console.WriteLine("------- iterations: " + iterations + "-------");
for (int i = 1; i <= iterations; ++i)
{
itemsQueue.Add(i);
}
while (true)
{
char c = Console.ReadKey().KeyChar;
if (c == 's')
{
break;
}
else
{
++iterations;
Console.WriteLine("adding: " + iterations);
itemsQueue.Add(iterations);
}
}
itemsQueue.CompleteAdding();
completion.Task.Wait();
Console.WriteLine("Done!");
Console.ReadKey();
itemsQueue.Dispose();
}
}
public class ImmediateOrderedPartitioner<T> : OrderablePartitioner<T>
{
private readonly IEnumerable<T> _consumingEnumerable;
private readonly Ordering _ordering = new Ordering();
public ImmediateOrderedPartitioner(BlockingCollection<T> collection) : base(true, true, true)
{
_consumingEnumerable = collection.GetConsumingEnumerable();
}
private class Ordering
{
public int Order = -1;
}
private class MyEnumerator<S> : IEnumerator<KeyValuePair<long, S>>
{
private readonly object _orderLock = new object();
private readonly IEnumerable<S> _enumerable;
private KeyValuePair<long, S> _current;
private bool _hasItem;
private Ordering _ordering;
public MyEnumerator(IEnumerable<S> consumingEnumerable, Ordering ordering)
{
_enumerable = consumingEnumerable;
_ordering = ordering;
}
public KeyValuePair<long, S> Current
{
get
{
if (_hasItem)
{
return _current;
}
else
throw new InvalidOperationException();
}
}
public void Dispose()
{
}
object System.Collections.IEnumerator.Current
{
get
{
return Current;
}
}
public bool MoveNext()
{
lock (_orderLock)
{
bool canMoveNext = false;
var next = _enumerable.Take(1).FirstOrDefault(s => { canMoveNext = true; return true; });
if (canMoveNext)
{
_current = new KeyValuePair<long, S>(++_ordering.Order, next);
_hasItem = true;
++_ordering.Order;
}
else
{
_hasItem = false;
}
return canMoveNext;
}
}
public void Reset()
{
throw new NotSupportedException();
}
}
public override IList<IEnumerator<KeyValuePair<long, T>>> GetOrderablePartitions(int partitionCount)
{
var result = new List<IEnumerator<KeyValuePair<long,T>>>();
//for (int i = 0; i < partitionCount; ++i)
//{
// result.Add(new MyEnumerator<T>(_consumingEnumerable, _ordering));
//}
//share the enumerator between partitions in this case to maintain
//the proper locking on ordering.
var enumerator = new MyEnumerator<T>(_consumingEnumerable, _ordering);
for (int i = 0; i < partitionCount; ++i)
{
result.Add(enumerator);
}
return result;
}
public override bool SupportsDynamicPartitions
{
get
{
return false;
}
}
public override IEnumerable<T> GetDynamicPartitions()
{
throw new NotImplementedException();
return base.GetDynamicPartitions();
}
public override IEnumerable<KeyValuePair<long, T>> GetOrderableDynamicPartitions()
{
throw new NotImplementedException();
return base.GetOrderableDynamicPartitions();
}
public override IList<IEnumerator<T>> GetPartitions(int partitionCount)
{
throw new NotImplementedException();
return base.GetPartitions(partitionCount);
}
}
class Program
{
static void Main(string[] args)
{
BlockingCollection<int> itemsQueue = new BlockingCollection<int>();
var partitioner = new ImmediateOrderedPartitioner<int>(itemsQueue);
Random random = new Random();
var results = partitioner
.AsParallel()
.AsOrdered()
.WithMergeOptions(ParallelMergeOptions.NotBuffered)
//.WithDegreeOfParallelism(1)
.Select(i =>
{
int work = 0;
Console.WriteLine("Working on " + i);
for (int busy = 0; busy <= 90000000; ++busy) { ++work; };
Console.WriteLine("Finished " + i);
return i;
});
TaskCompletionSource<bool> completion = new TaskCompletionSource<bool>();
Task.Factory.StartNew(() =>
{
foreach (int i in results)
{
Console.WriteLine("Result Available: " + i);
}
completion.SetResult(true);
});
int iterations;
iterations = 1; // random.Next(5, 50);
Console.WriteLine("------- iterations: " + iterations + "-------");
for (int i = 1; i <= iterations; ++i)
{
itemsQueue.Add(i);
}
while (true)
{
char c = Console.ReadKey().KeyChar;
if (c == 's')
{
break;
}
else
{
++iterations;
Console.WriteLine("adding: " + iterations);
itemsQueue.Add(iterations);
}
}
itemsQueue.CompleteAdding();
completion.Task.Wait();
Console.WriteLine("Done!");
Console.ReadKey();
itemsQueue.Dispose();
}
}
public class QueuedItem<TInput, TResult>
{
private readonly object _lockObject = new object();
private TResult _result;
private readonly TInput _input;
private readonly TResult _notfinished;
internal readonly bool IsEndQueue = false;
internal QueuedItem()
{
IsEndQueue = true;
}
public QueuedItem(TInput input, TResult notfinished)
{
_input = input;
_notfinished = notfinished;
_result = _notfinished;
}
public TResult ReadResult()
{
lock (_lockObject)
{
if (!IsResultReady)
throw new InvalidOperationException("Check IsResultReady before calling ReadResult()");
return _result;
}
}
public void WriteResult(TResult value)
{
lock (_lockObject)
{
if (IsResultReady)
throw new InvalidOperationException("Result has already been written");
_result = value;
}
}
public TInput Input { get { return _input; } }
public bool IsResultReady
{
get
{
lock (_lockObject)
{
return !object.Equals(_result, _notfinished) || IsEndQueue;
}
}
}
}
public class ParallelImmediateOrderedProcessingQueue<TInput, TResult>
{
private readonly ReaderWriterLockSlim _addLock = new ReaderWriterLockSlim();
private readonly object _readingResultsLock = new object();
private readonly ConcurrentQueue<QueuedItem<TInput, TResult>> _concurrentQueue = new ConcurrentQueue<QueuedItem<TInput, TResult>>();
bool _isFinishedAdding = false;
private readonly TResult _notFinished;
private readonly Action<QueuedItem<TInput, TResult>> _processor;
/// <param name="notFinished">A value that indicates the result is not yet finished</param>
/// <param name="processor">Must call SetResult() on argument when finished.</param>
public ParallelImmediateOrderedProcessingQueue(TResult notFinished, Action<QueuedItem<TInput, TResult>> processor)
{
_notFinished = notFinished;
_processor = processor;
}
public event Action ResultsReady = delegate { };
private void SignalResult()
{
QueuedItem<TInput, TResult> item;
if (_concurrentQueue.TryPeek(out item) && item.IsResultReady)
{
ResultsReady();
}
}
public void Add(TInput input)
{
bool shouldThrow = false;
_addLock.EnterReadLock();
{
shouldThrow = _isFinishedAdding;
if (!shouldThrow)
{
var queuedItem = new QueuedItem<TInput, TResult>(input, _notFinished);
_concurrentQueue.Enqueue(queuedItem);
Task.Factory.StartNew(() => { _processor(queuedItem); SignalResult(); });
}
}
_addLock.ExitReadLock();
if (shouldThrow)
throw new InvalidOperationException("An attempt was made to add an item, but adding items was marked as completed");
}
public IEnumerable<TResult> ConsumeReadyResults()
{
//lock necessary to preserve ordering
lock (_readingResultsLock)
{
QueuedItem<TInput, TResult> queuedItem;
while (_concurrentQueue.TryPeek(out queuedItem) && queuedItem.IsResultReady)
{
if (!_concurrentQueue.TryDequeue(out queuedItem))
throw new ApplicationException("this shouldn't happen");
if (queuedItem.IsEndQueue)
{
_completion.SetResult(true);
}
else
{
yield return queuedItem.ReadResult();
}
}
}
}
public void CompleteAddingItems()
{
_addLock.EnterWriteLock();
{
_isFinishedAdding = true;
var queueCompletion = new QueuedItem<TInput, TResult>();
_concurrentQueue.Enqueue(queueCompletion);
Task.Factory.StartNew(() => { SignalResult(); });
}
_addLock.ExitWriteLock();
}
TaskCompletionSource<bool> _completion = new TaskCompletionSource<bool>();
public void WaitForCompletion()
{
_completion.Task.Wait();
}
}
class Program
{
static void Main(string[] args)
{
const int notFinished = int.MinValue;
var processingQueue = new ParallelImmediateOrderedProcessingQueue<int, int>(notFinished, qi =>
{
int work = 0;
Console.WriteLine("Working on " + qi.Input);
//simulate work
int maxBusy = 90000000 - (10 * (qi.Input % 3));
for (int busy = 0; busy <= maxBusy; ++busy) { ++work; };
Console.WriteLine("Finished " + qi.Input);
qi.WriteResult(qi.Input);
});
processingQueue.ResultsReady += new Action(() =>
{
Task.Factory.StartNew(() =>
{
foreach (int result in processingQueue.ConsumeReadyResults())
{
Console.WriteLine("Results Available: " + result);
}
});
});
int iterations = new Random().Next(5, 50);
Console.WriteLine("------- iterations: " + iterations + "-------");
for (int i = 1; i <= iterations; ++i)
{
processingQueue.Add(i);
}
while (true)
{
char c = Console.ReadKey().KeyChar;
if (c == 's')
{
break;
}
else
{
++iterations;
Console.WriteLine("adding: " + iterations);
processingQueue.Add(iterations);
}
}
processingQueue.CompleteAddingItems();
processingQueue.WaitForCompletion();
Console.WriteLine("Done!");
Console.ReadKey();
}
}