C#中的并行迭代?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/523194/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Parallel iteration in C#?
提问by recursive
Is there a way to do foreach
style iteration over parallel enumerables in C#? For subscriptable lists, I know one could use a regular for
loop iterating an int over the index range, but I really prefer foreach
to for
for a number of reasons.
有没有办法foreach
在 C# 中对并行枚举进行样式迭代?对于可下标列表,我知道可以使用for
在索引范围内迭代 int的常规循环,但出于多种原因,我真的更喜欢foreach
这样for
做。
Bonus points if it works in C# 2.0
如果它在 C# 2.0 中工作,则加分
采纳答案by Zooba
Short answer, no. foreach
works on only one enumerable at a time.
简短的回答,没有。foreach
一次只对一个可枚举对象起作用。
However, if you combine your parallel enumerables into a single one, you can foreach
over the combined. I am not aware of any easy, built in method of doing this, but the following should work (though I have not tested it):
但是,如果将并行可枚举项合并为一个,则可以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();
}
}
Then you can foreach
over the returned enumerable:
然后你可以foreach
遍历返回的枚举:
foreach(var v in Combine(en1, en2, en3))
{
// Remembering that v is an array of the type contained in en1,
// en2 and en3.
}
回答by Jon Skeet
Zooba's answer is good, but you might also want to look at the answers to "How to iterate over two arrays at once".
Zooba 的回答很好,但您可能还想查看“如何一次迭代两个数组”的答案。
回答by Hosam Aly
Would this work for you?
这对你有用吗?
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
}
For C# 2.0, you need to convert the lambda expressions above to delegates.
对于 C# 2.0,您需要将上面的 lambda 表达式转换为委托。
Note: This utility method uses background threads. You may want to modify it to use foreground threads, and probably you'll want to wait till all threads finish. If you do that, I suggest you create sources.Length - 1
threads, and use the current executing thread for the last (or first) source.
注意:此实用程序方法使用后台线程。您可能希望修改它以使用前台线程,并且您可能希望等到所有线程完成。如果您这样做,我建议您创建sources.Length - 1
线程,并将当前正在执行的线程用作最后一个(或第一个)源。
(I wish I could include waiting for threads to finish in my code, but I'm sorry that I don't know how to do that yet. I guess you should use a WaitHandle
Thread.Join()
.)
(我希望我可以在我的代码中包含等待线程完成,但很抱歉我还不知道如何做到这一点。我想你应该使用 一种 WaitHandle
Thread.Join()
.)
回答by Andre
.NET 4's BlockingCollection makes this pretty easy. Create a BlockingCollection, return its .GetConsumingEnumerable() in the enumerable method. Then the foreach simply adds to the blocking collection.
.NET 4 的 BlockingCollection 使这变得非常容易。创建一个 BlockingCollection,在 enumerable 方法中返回它的 .GetConsumingEnumerable()。然后 foreach 简单地添加到阻塞集合中。
E.g.
例如
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();
}
Hope this helps. BTW I posted a blog about thislast night
希望这可以帮助。BTW我发布了关于这个博客昨晚
Andre
安德烈
回答by Rob Volk
I wrote an implementation of EachParallel() from the .NET4 Parallel library. It is compatible with .NET 3.5: Parallel ForEach Loop in C# 3.5Usage:
我从 .NET4 Parallel 库中编写了一个 EachParallel() 的实现。它与 .NET 3.5 兼容: C# 3.5 中的并行 ForEach 循环用法:
string[] names = { "cartman", "stan", "kenny", "kyle" };
names.EachParallel(name =>
{
try
{
Console.WriteLine(name);
}
catch { /* handle exception */ }
});
Implementation:
执行:
/// <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);
}
}
}
回答by mafu
If you want to stick to the basics - I rewrote the currently accepted answer in a simpler way:
如果您想坚持基础知识 - 我以更简单的方式重写了当前接受的答案:
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 ();
}