[英]How to process items in collection parallely and serially both
我有一個集合,其中包含要處理的元素,最多只能同時處理四個元素。 在運行時,所有進程一起啟動,並且都進入等待狀態。 一次只處理四個元素。
問題在於處理元素是隨機選擇的,因為所有線程都在等待資源釋放。 意味着第一個元素可以是集合中的最后一個。
但是,我需要處理元素以便它們在集合中。
請告訴我如何實現?
我正在使用TPL和C#4.0
對於並行性,始終存在定義“按順序”含義的問題。 假設您有100個項目的集合。 按照您的要求“一次按順序4”處理它們可能意味着:
松散排序:使用4個線程並按原始集合的順序發出任務。
在這種情況下,您可以使用:
ParallelOptions po = new ParallelOptions() { MaxDegreeOfParallelism = 4 }; Parallel.ForEach(list.AsParallel().AsOrdered(), po, (item) => { // code });
在任務不平衡的情況下,這將很快失去原始順序,因為某些線程可能會在繁重的任務上落后,但是任務將按順序分配。
嚴格排序:按4組的順序處理它們,如下所示:
0 1 2 3 4 tasks _____________________________ barrier 4 5 6 7 4 tasks _____________________________ barrier etc.
在這種情況下,您可以設置障礙:
Barrier b = new Barrier(4); ParallelOptions po = new ParallelOptions() { MaxDegreeOfParallelism = 4 }; Parallel.ForEach(list.AsParallel().AsOrdered(), po, (item) => { // code b.SignalAndWait(); });
盡管您必須確保任務數是4的倍數,否則在最后一次迭代時不會顯示障礙。
在單個任務中處理4個項目:您可以創建一個封裝原始列表中4個項目的任務對象,然后像在第一種情況下那樣執行一個簡單的Parallel.ForEach
(即,每個線程將作為單個事件的一部分順序處理4個項目)任務)。 這將按4個一組的順序發布任務,但是如果任務花費的時間太長,可能還會導致某些線程滯后。
我不清楚“隨機選擇元素”到底在做什么。 但是,如果您使用Paralle.ForEach()
,它將嘗試提高效率,因此會以某種方式對輸入序列進行分區。 如果輸入序列是IList<T>
,它將使用范圍分區,否則,將使用塊分區(請參閱PLINQ中的塊分區與范圍分區 )。
如果要按順序處理項目,則可以使用自定義分區程序配置Parallel.ForEach()
,該程序將集合划分為大小為1的塊。
但是由於您在這里實際上並不需要Parallel.ForEach()
,因此可能更簡單的解決方案是創建4個任務來逐項處理這些任務。 為了進行同步,可以使用BlockingCollection
。 就像是:
public static class ParallelOrdered
{
public static void ForEach<T>(IEnumerable<T> collection, Action<T> action, int degreeOfParallelism)
{
var blockingCollection = new BlockingCollection<T>();
foreach (var item in collection)
blockingCollection.Add(item);
blockingCollection.CompleteAdding();
var tasks = new Task[degreeOfParallelism];
for (int i = 0; i < degreeOfParallelism; i++)
{
tasks[i] = Task.Factory.StartNew(
() =>
{
foreach (var item in blockingCollection.GetConsumingEnumerable())
action(item);
});
}
Task.WaitAll(tasks);
}
}
這就是我完成這項任務的方式
public delegate void ProcessFinished(IParallelProcess process);
public interface IParallelProcess
{
void Start();
event ProcessFinished ProcessFinished;
}
public class ParallelProcessBasket : ConcurrentQueue<IParallelProcess>
{
public void Put(IParallelProcess process)
{
base.Enqueue(process);
}
public IParallelProcess Get()
{
IParallelProcess process = null;
base.TryDequeue(out process);
return process;
}
}
public class ParallelProcessor<T> where T : class
{
private ParallelProcessBasket basket;
private readonly int MAX_DEGREE_OF_PARALLELISM;
private Action<T> action;
public ParallelProcessor(int degreeOfParallelism, IEnumerable<IParallelProcess> processes, Action<T> action)
{
basket = new ParallelProcessBasket();
this.action = action;
processes.ToList().ForEach(
(p) =>
{
basket.Enqueue(p);
p.ProcessFinished += new ProcessFinished(p_ProcessFinished);
});
MAX_DEGREE_OF_PARALLELISM = degreeOfParallelism;
}
private void p_ProcessFinished(IParallelProcess process)
{
if (!basket.IsEmpty)
{
T element = basket.Get() as T;
if (element != null)
{
Task.Factory.StartNew(() => action(element));
}
}
}
public void StartProcessing()
{
// take first level of iteration
for (int cnt = 0; cnt < MAX_DEGREE_OF_PARALLELISM; cnt++)
{
if (!basket.IsEmpty)
{
T element = basket.Get() as T;
if (element != null)
{
Task.Factory.StartNew(() => action(element));
}
}
}
}
}
static void Main(string[] args)
{
ParallelProcessor<ParallelTask> pr = new ParallelProcessor<ParallelTask>(Environment.ProcessorCount, collection, (e) => e.Method1());
pr.StartProcessing();
}
謝謝..
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.