Автоматическое выполнение зацикливания - PullRequest
0 голосов
/ 19 августа 2009

Я не знаю, имеет ли название смысл, но в приложении, которое я пишу, есть много (расширенных) методов. Простой пример:

Предметы:

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. Таким образом, я хочу сам управлять параллелизацией (записывать параллельно).

Как мне сделать это наиболее эффективным способом, или самым чистым, или наиболее приемлемым способом, или в сочетании?

Во-первых, я бы предпочел не определять другой метод расширения, который принимает ImmutableList внутри каждого класса (Burner и т. Д.), Потому что их сотни на сотни, и они, вероятно, будут выглядеть одинаково. Но я открыт для идей.

Также весь код мой, поэтому я могу изменить / добавить что угодно в любой части кода, не только в методах расширения.

Ответы [ 4 ]

2 голосов
/ 20 августа 2009

Вы можете найти эту статью интересной для чтения. В нем обсуждается, как может работать параллельный foreach, делая это самостоятельно и используя Параллельные расширения CTP для .NET 3.5. С 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 голосов
/ 19 августа 2009

Что не так с

matters.ForEach(Burner.Burn);

с вашей собственной реализацией ForEach?

1 голос
/ 20 августа 2009

Вот простой класс, который выполняет параллельные итерации.

Эмре Айдинсерен

Использование:

Parallel.ForEach (мальчики, материя => Материя.Burn ());

или

значения. ParallelForEach (вещество => вопрос.Бурн ());

/// <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);
    }

}
0 голосов
/ 19 августа 2009

создайте свое собственное расширение ForEachParallel, тогда, если вы не хотите использовать PLinq или что-то еще

...