varios parametros nicosiored net multihilos hilos ejecutar con asp c# thread-safety ienumerable

parametros - C#: ¿Cómo puedo hacer que un hilo IEnumerable<T> sea seguro?



multihilos en c# (6)

Acabo de probar este pedacito de código:

static IEnumerable<int> getNums() { Console.WriteLine("IENUM - ENTER"); for (int i = 0; i < 10; i++) { Console.WriteLine(i); yield return i; } Console.WriteLine("IENUM - EXIT"); } static IEnumerable<int> getNums2() { try { Console.WriteLine("IENUM - ENTER"); for (int i = 0; i < 10; i++) { Console.WriteLine(i); yield return i; } } finally { Console.WriteLine("IENUM - EXIT"); } }

getNums2 () siempre llama a la parte final del código. Si desea que su IEnumerable sea seguro para subprocesos, agregue los bloqueos de subprocesos que desee en lugar de writelines, eliminándolos mediante ReaderWriterSlimLock, Semaphore, Monitor, etc.

Digamos que tengo este sencillo método:

public IEnumerable<uint> GetNumbers() { uint n = 0; while(n < 100) yield return n++; }

¿Cómo harías seguro este hilo? Y con eso me refiero a que obtendrías ese enumerador una vez, y harías que múltiples hilos manejen todos los números sin que nadie obtenga duplicados.

Supongo que es necesario usar un bloqueo en alguna parte, pero ¿dónde debe estar ese bloqueo para que un bloque iterador sea seguro para subprocesos? ¿Qué necesita recordar, en general, si quiere un hilo seguro IEnumerable<T> ? O mejor dicho, supongo que sería un hilo de seguridad IEnumerator<T> ...?


Bueno, no estoy seguro, pero tal vez con algunos bloqueos en la persona que llama?

Borrador:

Monitor.Enter(syncRoot); foreach (var item in enumerable) { Monitor.Exit(syncRoot); //Do something with item Monitor.Enter(syncRoot); } Monitor.Exit(syncRoot);


Estaba pensando que no puede hacer que la palabra clave yield segura para subprocesos, a menos que la haga depender de una fuente de valores ya segura para subprocesos:

public interface IThreadSafeEnumerator<T> { void Reset(); bool TryMoveNext(out T value); } public class ThreadSafeUIntEnumerator : IThreadSafeEnumerator<uint>, IEnumerable<uint> { readonly object sync = new object(); uint n; #region IThreadSafeEnumerator<uint> Members public void Reset() { lock (sync) { n = 0; } } public bool TryMoveNext(out uint value) { bool success = false; lock (sync) { if (n < 100) { value = n++; success = true; } else { value = uint.MaxValue; } } return success; } #endregion #region IEnumerable<uint> Members public IEnumerator<uint> GetEnumerator() { //Reset(); // depends on what behaviour you want uint value; while (TryMoveNext(out value)) { yield return value; } } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { //Reset(); // depends on what behaviour you want uint value; while (TryMoveNext(out value)) { yield return value; } } #endregion }

Deberá decidir si cada iniciación típica de un enumerador debe restablecer la secuencia, o si el código del cliente debe hacer eso.


Hay un problema inherente al hacerlo, porque IEnumerator<T> tiene tanto MoveNext() como Current . Realmente quieres una sola llamada como:

bool TryMoveNext(out T value)

en ese punto puede moverse atómicamente al siguiente elemento y obtener un valor. Implementar eso y seguir usando el yield podría ser complicado ... Sin embargo, lo voy a tener que pensar. Creo que tendrías que ajustar el iterador "no seguro para hilos" en uno seguro para subprocesos que realizara MoveNext() y Current para implementar la interfaz que se muestra arriba. No sé cómo volvería a envolver esta interfaz en IEnumerator<T> para que pueda usarla en foreach ...

Si usa .NET 4.0, Parallel Extensions puede ayudarlo; sin embargo, deberá explicar más acerca de lo que está tratando de hacer.

Este es un tema interesante: podría tener que escribir sobre él ...

EDITAR: ahora he blogueado sobre esto con dos enfoques.


Puede devolver una secuencia completa cada vez en lugar de usar yield:

return Enumerable.Range(0, 100).Cast<uint>().ToArray();


Supongo que necesita un Enumerator para guardar el hilo, por lo que probablemente debería implementarlo.