簡體   English   中英

如何並行和串行地處理集合中的項目

[英]How to process items in collection parallely and serially both

我有一個集合,其中包含要處理的元素,最多只能同時處理四個元素。 在運行時,所有進程一起啟動,並且都進入等待狀態。 一次只處理四個元素。

問題在於處理元素是隨機選擇的,因為所有線程都在等待資源釋放。 意味着第一個元素可以是集合中的最后一個。

但是,我需要處理元素以便它們在集合中。

請告訴我如何實現?

我正在使用TPL和C#4.0

對於並行性,始終存在定義“按順序”含義的問題。 假設您有100個項目的集合。 按照您的要求“一次按順序4”處理它們可能意味着:

  1. 松散排序:使用4個線程並按原始集合的順序發出任務。

    在這種情況下,您可以使用:

     ParallelOptions po = new ParallelOptions() { MaxDegreeOfParallelism = 4 }; Parallel.ForEach(list.AsParallel().AsOrdered(), po, (item) => { // code }); 

    在任務不平衡的情況下,這將很快失去原始順序,因為某些線程可能會在繁重的任務上落后,但是任務將按順序分配。

  2. 嚴格排序:按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的倍數,否則在最后一次迭代時不會顯示障礙。

  3. 在單個任務中處理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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM