C#中的并行迭代?

C#中的并行迭代?,c#,loops,foreach,iteration,C#,Loops,Foreach,Iteration,有没有一种方法可以对C#中的并行枚举进行foreach风格的迭代?对于可订阅列表,我知道可以使用常规的For循环在索引范围内迭代一个int,但是我确实更喜欢foreach而不是,原因有很多 如果它在C#2.0中工作,则可获得加分。简短回答,否。foreach一次只能在一个可枚举项上工作 但是,如果将并行枚举组合成单个枚举,则可以在组合的枚举中使用foreach。我不知道有什么简单的内置方法可以做到这一点,但以下方法应该可以工作(尽管我没有测试过): Zooba的答案很好,但您可能还想看看的答案。

有没有一种方法可以对C#中的并行枚举进行
foreach
风格的迭代?对于可订阅列表,我知道可以使用常规的
For
循环在索引范围内迭代一个int,但是我确实更喜欢
foreach
而不是
,原因有很多


如果它在C#2.0中工作,则可获得加分。简短回答,否。
foreach
一次只能在一个可枚举项上工作

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


Zooba的答案很好,但您可能还想看看的答案。

这对您有用吗

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
}
公共静态类并行
{
公共静态void ForEach(IEnumerable[]来源,
行动(行动)
{
foreach(源中可枚举的var)
{
ThreadPool.QueueUserWorkItem(源=>{
foreach(在(IEnumerable)源中的变量项)
行动(项目);
},可列举);
}
}
}
//示例用法:
静态void Main()
{
字符串[]s1={“1”、“2”、“3”};
字符串[]s2={“4”、“5”、“6”};
IEnumerable[]源={s1,s2};
Parallel.ForEach(sources,s=>Console.WriteLine);
Thread.Sleep(0);//允许后台线程工作
}
对于C#2.0,需要将上面的lambda表达式转换为委托

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


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

.NET4的BlockingCollection使这非常容易。创建BlockingCollection,在enumerable方法中返回其.GetConsumingEnumerable()。然后foreach简单地添加到阻塞集合中

例如

private BlockingCollection m_data=new BlockingCollection();
公共IEnumerable GetData(IEnumerable源)
{
Task.Factory.StartNew(()=>ParallelGetData(sources));
返回m_数据。getConsuminegumerable();
}
私有void ParallelGetData(IEnumerable源)
{
foreach(源中的var源)
{
foreach(源中的var项)
{
m_数据。添加(项目);
};
}
//添加完成后,枚举现在可以停止
m_data.CompleteAdding();
}
希望这有帮助。 顺便说一句,我昨晚


Andre

我从.NET4并行库中编写了EachParallel()的实现。它与.NET 3.5兼容: 用法:

实施:

/// <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);
        }
    }
}
//
///并行枚举列表中的每个项
/// 
每个并行的公共静态void(此IEnumerable列表,操作)
{
//枚举列表,使其在执行期间不会更改
list=list.ToArray();
var count=list.count();
如果(计数=0)
{
返回;
}
否则如果(计数=1)
{
//如果只有一个元素,就执行它
动作(list.First());
}
其他的
{
//在它自己的线程中启动每个方法
const int MaxHandles=64;
对于(var offset=0;offset
{
int methodIndex=(int)((对象[])数据)[0];
//执行该方法并传入枚举项
动作((T)((对象[])数据)[1]);
//告诉呼叫线程我们完成了
resetEvents[methodIndex].Set();
}),新对象[]{i,item});
i++;
}
//等待所有线程执行
WaitHandle.WaitAll(重置事件);
}
}
}

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

    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 ();
    }
公共静态IEnumerable组合(此IEnumerable源)
{
变量枚举=源
.Select(s=>s.GetEnumerator())
.ToArray();
而(enums.All(e=>e.MoveNext()){
产生返回enums.Select(e=>e.Current.ToArray();
}
}
公共静态IEnumerable合并(params IEnumerable[]源)
{
返回sources.Combine();
}

for循环不是更简单、更短、可读性更强的解决方案,而不是下面的组合响应吗?在这种情况下,您选择foreach的原因是什么?Ruby 1.9中刚刚出现了并行迭代,所以我敢打赌它现在还没有出现在C中。。尽管如此,LISP还是说:)我不确定我是否正确理解了这个问题。您是在尝试并行迭代多个枚举,还是在尝试循环一个枚举,并行处理不同的项?我说的是并行多个枚举。就像平行排列的铅笔和纸张。我希望每支铅笔都写在相应的纸上。为什么选择object[]而不是IEnumerable作为参数类型?会的
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();
}
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);
        }
    }
}
    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 ();
    }