我刚刚开始使用任务并行库。有问题的任务是以尽可能平行的方式处理结果,但保持结果的顺序。
此外,可以随时添加项目,直到设置了标志,表示不再接受任何项目。
此外,一些客户需要在完成所有结果后收到通知(只有在不再接受任何项目时才会通知)。
我已经提出了以下简化的样本,这似乎在我的所有测试中都很好。
class Program
{
static void Main(string[] args)
{
for (int i = 1; i < random.Next(5, 21); ++i)
{
AddItem(i);
}
finishedAddingItems = true;
completion.Task.Wait();
Console.WriteLine("Finished");
Console.ReadKey();
}
static TaskCompletionSource<bool> completion =
new TaskCompletionSource<bool>();
static bool finishedAddingItems = false;
static Random random = new Random();
class QueueResult
{
public int data;
public int IsFinished;
}
static ConcurrentQueue<QueueResult> queue =
new ConcurrentQueue<QueueResult>();
static object orderingLockObject = new object();
static void AddItem(int i)
{
var queueItem = new QueueResult { data = i, IsFinished = 0 };
queue.Enqueue(queueItem);
Task.Factory
.StartNew(() =>
{
for (int busy = 0;
busy <= random.Next(9000000, 90000000);
++busy)
{ };
Interlocked.Increment(ref queueItem.IsFinished);
})
.ContinueWith(t =>
{
QueueResult result;
//the if check outside the lock is to avoid tying up resources
//needlessly, since only one continuation can actually process
//the queue at a time.
if (queue.TryPeek(out result)
&& result.IsFinished == 1)
{
lock (orderingLockObject)
{
while (queue.TryPeek(out result)
&& result.IsFinished == 1)
{
Console.WriteLine(result.data);
queue.TryDequeue(out result);
}
if (finishedAddingItems && queue.Count == 0)
{
completion.SetResult(true);
}
}
}
});
}
}
但是,我无法说服自己是否存在可能无法处理项目的潜在竞争条件?
答案 0 :(得分:2)
我认为您的代码可能无法正常运行,因为您没有将IsFinished
声明为volatile
,而是直接在锁定之外访问它。在任何情况下,正确使用double-checked locking都很难,所以除非你真的需要,否则你不应该这样做。
此外,您的代码也非常混乱(在一个类中使用int
而不是bool
,不必要的ContinueWith()
,...)并且至少包含一个以上的线程 - 安全问题(Random
不是线程安全的。)
由于这一切,我建议您了解TPL的更高级部分。在您的情况下,PLINQ听起来像是正确的解决方案:
var source = Enumerable.Range(1, random.Next(5, 21)); // or some other collection
var results = source.AsParallel()
.AsOrdered()
.WithMergeOptions(ParallelMergeOptions.NotBuffered)
.Select(i => /* perform your work here */);
foreach (int i in results)
Console.WriteLine(i);