tareas programacion paralelismo paralela hilos ejemplos asincrona c# loops foreach iteration

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