paralelismo - programacion asincrona c# ejemplos
IteraciĆ³n paralela en C#? (6)
Que este trabajo para usted?
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
}
Para C # 2.0, necesita convertir las expresiones lambda anteriores a delegados.
Nota: Este método de utilidad utiliza subprocesos de fondo. Es posible que desee modificarlo para utilizar subprocesos de primer plano, y probablemente desee esperar hasta que finalicen todos los subprocesos. Si lo hace, le sugiero que cree las sources.Length - 1
subprocesos, y utilice el subproceso de ejecución actual para la última (o primera) fuente.
(Me gustaría poder incluir la espera de que los hilos terminen en mi código, pero lamento que todavía no sé cómo hacerlo. Supongo que deberías usar un WaitHandle
Thread.Join()
)
¿Hay alguna manera de hacer iteración de estilo foreach
sobre enumerables paralelos en C #? Para las listas subscripibles, sé que se podría usar un bucle for
iterar un int sobre el rango de índice, pero realmente prefiero foreach
to for
varias razones.
Puntos de bonificación si funciona en C # 2.0
Escribí una implementación de EachParallel () desde la biblioteca .NET4 Parallel. Es compatible con .NET 3.5: Paralelo ForEach Loop en C # 3.5 Uso:
string[] names = { "cartman", "stan", "kenny", "kyle" };
names.EachParallel(name =>
{
try
{
Console.WriteLine(name);
}
catch { /* handle exception */ }
});
Implementación:
/// <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);
}
}
}
Respuesta corta, no. foreach
funciona solo en un enumerable a la vez.
Sin embargo, si combina sus enumeraciones paralelas en una sola, puede foreach
sobre el combinado. No conozco ningún método fácil e integrado para hacerlo, pero lo siguiente debería funcionar (aunque no lo he probado):
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();
}
}
Entonces puede foreach
sobre el enumerable devuelto:
foreach(var v in Combine(en1, en2, en3))
{
// Remembering that v is an array of the type contained in en1,
// en2 and en3.
}
Si quiere seguir con lo básico, reescribí la respuesta actualmente aceptada de una manera más simple:
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 ();
}
BlockkingCollection de .NET 4 lo hace bastante fácil. Crea un BlockingCollection, devuelve su .GetConsumingEnumerable () en el método enumerable. Entonces el foreach simplemente se agrega a la colección de bloqueo.
P.ej
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();
}
Espero que esto ayude. Por cierto, publiqué un blog sobre esta última noche
Andre
La respuesta de Zooba es buena, pero es posible que también desee ver las respuestas a "Cómo iterar en dos matrices a la vez" .