代码之家  ›  专栏  ›  技术社区  ›  recursive

C#中的并行迭代?

  •  7
  • recursive  · 技术社区  · 17 年前

    有办法吗 foreach C#中并行枚举的风格迭代?对于可下标列表,我知道可以使用常规 for 循环在索引范围内迭代一个int,但我更喜欢 循环 出于多种原因。

    如果在C#2.0中运行,则可获得加分

    6 回复  |  直到 11 年前
        1
  •  11
  •   Andre    11 年前

    .NET 4的BlockingCollection使这变得非常容易。创建一个BlockingCollection,返回它的值。enumerable方法中的GetConsuringEnumerable()。然后foreach简单地添加到阻塞集合中。

    例如

    private BlockingCollection<T> m_data = new BlockingCollection<T>();
    
    public IEnumerable<T> GetData( IEnumerable<IEnumerable<T>> sources )
    {
        Task.Factory.StartNew( () => ParallelGetData( sources ) );
        return m_data.GetConsumingEnumerable();
    }
    
    private void ParallelGetData( IEnumerable<IEnumerable<T>> sources )
    {
        foreach( var source in sources )
        {
            foreach( var item in source )
            {
                m_data.Add( item );
            };
        }
    
        //Adding complete, the enumeration can stop now
        m_data.CompleteAdding();
    }
    

    希望这能有所帮助。 顺便说一句 posted a blog about this 昨晚

    安德烈

        2
  •  9
  •   mafu    11 年前

    简短的回答,没有。 foreach 一次只处理一个可枚举项。

    但是,如果将并行枚举组合成一个,则可以 循环 在组合。我不知道有什么简单的内置方法可以做到这一点,但以下方法应该有效(尽管我没有测试过):

    public IEnumerable<TSource[]> Combine<TSource>(params object[] sources)
    {
        foreach(var o in sources)
        {
            // Choose your own exception
            if(!(o is IEnumerable<TSource>)) throw new Exception();
        }
    
        var enums =
            sources.Select(s => ((IEnumerable<TSource>)s).GetEnumerator())
            .ToArray();
    
        while(enums.All(e => e.MoveNext()))
        {
            yield return enums.Select(e => e.Current).ToArray();
        }
    }
    

    那么你可以 循环 在返回的枚举值之上:

    foreach(var v in Combine(en1, en2, en3))
    {
        // Remembering that v is an array of the type contained in en1,
        // en2 and en3.
    }
    
        3
  •  3
  •   Community Mohan Dere    8 年前

    Zooba的答案很好,但你可能也想看看以下问题的答案 "How to iterate over two arrays at once" .

        4
  •  3
  •   Rob Volk    16 年前

    我从中编写了EachParallel()的实现。NET4并行库。它与兼容。净值3.5: Parallel ForEach Loop in C# 3.5 用途:

    string[] names = { "cartman", "stan", "kenny", "kyle" };
    names.EachParallel(name =>
    {
        try
        {
            Console.WriteLine(name);
        }
        catch { /* handle exception */ }
    });
    

    实施:

    /// <summary>
    /// Enumerates through each item in a list in parallel
    /// </summary>
    public static void EachParallel<T>(this IEnumerable<T> list, Action<T> action)
    {
        // enumerate the list so it can't change during execution
        list = list.ToArray();
        var count = list.Count();
    
        if (count == 0)
        {
            return;
        }
        else if (count == 1)
        {
            // if there's only one element, just execute it
            action(list.First());
        }
        else
        {
            // Launch each method in it's own thread
            const int MaxHandles = 64;
            for (var offset = 0; offset < list.Count() / MaxHandles; offset++)
            {
                // break up the list into 64-item chunks because of a limitiation             // in WaitHandle
                var chunk = list.Skip(offset * MaxHandles).Take(MaxHandles);
    
                // Initialize the reset events to keep track of completed threads
                var resetEvents = new ManualResetEvent[chunk.Count()];
    
                // spawn a thread for each item in the chunk
                int i = 0;
                foreach (var item in chunk)
                {
                    resetEvents[i] = new ManualResetEvent(false);
                    ThreadPool.QueueUserWorkItem(new WaitCallback((object data) =>
                    {
                        int methodIndex = (int)((object[])data)[0];
    
                        // Execute the method and pass in the enumerated item
                        action((T)((object[])data)[1]);
    
                        // Tell the calling thread that we're done
                        resetEvents[methodIndex].Set();
                    }), new object[] { i, item });
                    i++;
                }
    
                // Wait for all threads to execute
                WaitHandle.WaitAll(resetEvents);
            }
        }
    }
    
        5
  •  1
  •   mafu    11 年前

    如果你想坚持基本原则,我以更简单的方式重写了目前接受的答案:

        public static IEnumerable<TSource[]> Combine<TSource> (this IEnumerable<IEnumerable<TSource>> sources)
        {
            var enums = sources
                .Select (s => s.GetEnumerator ())
                .ToArray ();
    
            while (enums.All (e => e.MoveNext ())) {
                yield return enums.Select (e => e.Current).ToArray ();
            }
        }
    
        public static IEnumerable<TSource[]> Combine<TSource> (params IEnumerable<TSource>[] sources)
        {
            return sources.Combine ();
        }
    
        6
  •  0
  •   Hosam Aly    17 年前

    这对你有用吗?

    public static class Parallel
    {
        public static void ForEach<T>(IEnumerable<T>[] sources,
                                      Action<T> action)
        {
            foreach (var enumerable in sources)
            {
                ThreadPool.QueueUserWorkItem(source => {
                    foreach (var item in (IEnumerable<T>)source)
                        action(item);
                }, enumerable);
            }
        }
    }
    
    // sample usage:
    static void Main()
    {
        string[] s1 = { "1", "2", "3" };
        string[] s2 = { "4", "5", "6" };
        IEnumerable<string>[] sources = { s1, s2 };
        Parallel.ForEach(sources, s => Console.WriteLine(s));
        Thread.Sleep(0); // allow background threads to work
    }
    

    对于C#2.0,您需要将上述lambda表达式转换为委托。

    注意:此实用程序方法使用后台线程。您可能希望修改它以使用前台线程,并且可能希望等待所有线程完成。如果你这样做,我建议你创建 sources.Length - 1 线程,并将当前执行线程用于最后一个(或第一个)源。

    (我希望我可以在代码中包含等待线程完成,但很抱歉,我还不知道该怎么做。我想你应该使用 WaitHandle Thread.Join() .)