自动实现循环

时间:2009-08-19 18:32:38

标签: c# .net class extension-methods parallel-processing

我不知道标题是否有意义,但在我写的应用程序中有很多(扩展)方法。一个简单的例子:

物件:

Matter (Burn, Explode, Destroy, Freeze, Heat, Cool)
Atom (Attach, Detach)
<many more>

自定义集合如:

ImmutableList<T>

以及类似的方法:

public static class Burner
{
    public static Matter Burn ( this Matter matter )
    {
        // matter is burning ...
    }
}

var matters = new ImmutableList<Matter>();
matters.Burn();

正如您所看到的,Burn可以在单个实体上运行,但仍会出现在ImmutableList上。这样我想自己管理并行化(并行刻录)。

我如何以最高效的方式,最干净,最易维护的方式,或组合使用?

首先,我宁愿不定义另一个在每个类(Burner等)中使用ImmutableList的扩展方法,因为有数百个像这样的它们可能看起来一样。但我对这些想法持开放态度。

此外,所有代码都是我的,所以我可以在代码的任何部分更改/添加任何内容,而不仅仅是扩展方法。

4 个答案:

答案 0 :(得分:2)

有什么问题
matters.ForEach(Burner.Burn);

使用您自己的ForEach实现?

答案 1 :(得分:2)

您可能会发现this article是一本有趣的读物。它讨论了并行foreach如何工作,既可以自己动手也可以使用.NET 3.5的Parallel extensions CTP。使用CTP,您可以这样做(例子来自上面的文章):

using System.Threading;

// A simple string collection
string[] numbers = { "One", "Two", "Three", "Four", "Five", "Six", "Seven",
  "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen"};

// equivalent to: foreach (string n in numbers)
Parallel.ForEach<string>(numbers, delegate(string n)
{
  Console.WriteLine("n={0}", n.ToString());
});

您应该犹豫在生产代码中使用CTP,除非它仅适用于您自己的项目(在这种情况下您可能应该尝试使用CTP)。

答案 2 :(得分:1)

这是一个以并行方式迭代的简单类。

Emre Aydinceren

用法:

Parallel.ForEach(事项,问题=&gt;问题.Burn());

matters.ParallelForEach(matter =&gt; matter.Burn());

/// <summary>
/// Provides concurrent processing on a sequence of elements
/// </summary>
public static class Parallel
{
    /// <summary>
    /// Number Of parallel tasks 
    /// </summary>
    public static int NumberOfParallelTasks;


    static Parallel()
    {
        NumberOfParallelTasks =  Environment.ProcessorCount < 65 ?  Environment.ProcessorCount : 64;  
    }

    /// <summary>
    /// Performs the specified action on each element of the sequence in seperate threads.
    /// </summary>
    /// <typeparam name="T">The type of the elements of source.</typeparam>
    /// <param name="source">A sequence that contains elements to perform action</param>
    /// <param name="action">The Action delegate to perform on each element of the IEnumerable.</param>
    public static void ForEach<T>( IEnumerable<T> source, Action<T> action )
    {
        if(source == null) return;

        //create a new stack for parallel task we want to run  , stack is very performant to add and read elements in sequence
        var stacks = new Stack<T>[NumberOfParallelTasks]; 

        //instantiate stacks
        for(var i = 0;i < NumberOfParallelTasks;i++)
        {
            stacks[i] = new Stack<T>();
        }

        var itemCount = 0;

        //spread items in source to all stacks while alternating between stacks
        foreach(var item in source)
        {
            stacks[itemCount++ % NumberOfParallelTasks].Push(item);
        }

        if(itemCount==0)return;

        //if we have less items than number of Parallel tasks we should only spun threads for active stacks
        var activeStackCount = itemCount < NumberOfParallelTasks ? itemCount : NumberOfParallelTasks;

        //events are used to notify thread pool completed
        var events = new ManualResetEvent[activeStackCount];

        for(var index = 0;index < activeStackCount;index++)
        {
            //assign index to a scope variable otherwise in multithreading index will not be consistant
            var listIndex = index;

            events[listIndex] = new ManualResetEvent(false); 

            ThreadPool.QueueUserWorkItem(delegate
            {
                //name the thread for debugging
                if(String.IsNullOrEmpty(Thread.CurrentThread.Name))
                {
                    Thread.CurrentThread.Name = String.Format("Parallel.ForEach Worker Thread No:{0}", listIndex);
                }

                try
                {   
                    //iterate through our stack 
                    var stack = stacks[listIndex];
                    foreach(var item in stack)
                    {
                        action(item); 
                    }   
                }
                finally
                {
                    //fire the event to signal WaitHandle that our thread is completed
                    events[listIndex].Set();
                }

            });
        }

        WaitAll(events);

    }

    private static void WaitAll(WaitHandle[] waitHandles)
    {
        if(Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
        {
            for(var index = 0;index < waitHandles.Length;index++) WaitHandle.WaitAny(waitHandles);
        }
        else
        {
            WaitHandle.WaitAll(waitHandles); 
        }
    }

    /// <summary>
    /// Performs the specified action on each element of the sequence in seperate threads.
    /// </summary>
    /// <typeparam name="T">The type of the elements of source.</typeparam>
    /// <param name="source">A sequence that contains elements to perform action</param>
    /// <param name="action">The Action delegate to perform on each element of the IEnumerable.</param>
    public static void  ParallelForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        ForEach(source, action);
    }

}

答案 3 :(得分:0)

创建自己的ForEachParallel扩展,如果你不想使用PLinq或其他东西