c# coroutine fiber

Coroutines en C#



fiber (5)

Creo que con el nuevo .NET 4.5 / C # 5 el patrón async / await debe satisfacer sus necesidades.

async Task<string> DownloadDocument(Uri uri) { var webClient = new WebClient(); var doc = await webClient.DownloadStringTaskAsync(url); // do some more async work return doc; }

Sugiero que visite http://channel9.msdn.com/Events/TechEd/Australia/Tech-Ed-Australia-2011/DEV411 para obtener más información. Es una gran presentación.

También http://msdn.microsoft.com/en-us/vstudio/gg316360 tiene una gran información.

Si está utilizando una versión anterior de .NET, hay un CTP asíncrono disponible para .NET más antiguo con una licencia activa para que pueda usarlo en entornos de producción. Aquí hay un enlace al CTP http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9983

Si no le gusta ninguna de las opciones anteriores, creo que podría seguir el patrón de iterador asíncrono como se describe aquí. http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9983

Estoy buscando formas de implementar co-rutinas (subprocesos programados por el usuario) en c #. Cuando usaba c ++ estaba usando fibras. Veo en internet que las fibras no existen en C #. Me gustaría obtener una funcionalidad similar.

¿Hay alguna manera "correcta" de implementar coroutines en c #?

He pensado en implementar esto utilizando subprocesos que adquieren una sola ejecución mutex + 1 en el subproceso del programador que libera este mutex para cada corrutina. Pero esto parece muy costoso (obliga a un cambio de contexto entre cada coroutine)

También he visto la funcionalidad del iterador de rendimiento, pero según tengo entendido, no se puede realizar dentro de una función interna (solo en la función de ienumerador original). Así que esto me hace poco bien.



Quizás te interese que this es una biblioteca que oculta el uso de coroutines. Por ejemplo para leer un archivo:

//Prepare the file stream FileStream sourceStream = File.Open("myFile.bin", FileMode.OpenOrCreate); sourceStream.Seek(0, SeekOrigin.End); //Invoke the task yield return InvokeTaskAndWait(sourceStream.WriteAsync(result, 0, result.Length)); //Close the stream sourceStream.Close();

Esta biblioteca usa un hilo para ejecutar todas las rutinas y permite llamar a la tarea para las operaciones verdaderamente asíncronas. Por ejemplo, para llamar a otro método como coroutine (también conocido como rendimiento para su devolución).

//Given the signature //IEnumerable<string> ReadText(string path); var result = new Container(); yield return InvokeLocalAndWait(() => _globalPathProvider.ReadText(path), container); var data = container.RawData as string;


Here hay un ejemplo del uso de hilos para implementar coroutines:

Así que hago trampa. Yo uso hilos, pero solo dejo que uno de ellos se ejecute a la vez. Cuando creo un coroutine, creo un hilo, y luego hago un apretón de manos que termina con una llamada a Monitor.Wait (), que bloquea el hilo coroutine. Ya no se ejecutará hasta que se desbloquee. Cuando es el momento de llamar a la rutina, hago un traspaso que termina con el hilo de llamada bloqueado, y el hilo de la rutina se puede ejecutar. El mismo tipo de transferencia en el camino de regreso.

Esas transferencias son un poco caras, en comparación con otras implementaciones. Si necesita velocidad, querrá escribir su propia máquina de estados y evitar todo este cambio de contexto. (O querrá usar un tiempo de ejecución que tenga en cuenta las fibras, ya que cambiar las fibras es bastante barato). Pero si desea un código expresivo, creo que las coroutinas son algo prometedoras.


Canaliza la pieza faltante

Las tuberías son la pieza faltante en relación con los canales en Golang. Los canales son en realidad lo que hace que la golang tick. Los canales son la herramienta de concurrencia central. Si está usando algo como una coroutine en C # pero utilizando primativos de sincronización de subprocesos (semáforo, monitor, interbloqueado, etc.), entonces no es lo mismo.

Casi lo mismo - tuberías, pero cocidas

8 años después, y .Net Standard (.Net Framework / .Net Core) es compatible con Pipelines [ https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io-in-net/] . Las tuberías son las preferidas para el procesamiento de la red. Aspcore ahora se clasifica entre las 11 tasas de solicitud de rendimiento de texto sin formato principales [ https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext] .

Microsoft recomienda las mejores prácticas para interactuar con el tráfico de red: los bytes de red esperados (IO de puerto de finalización) deben poner los datos en una canalización, y otro subproceso debe leer los datos de la tubería de forma asíncrona. Muchas tuberías se pueden usar en serie para varios procesos en el flujo de bytes. La canalización tiene un lector y un cursor de escritura, y el tamaño del búfer virtual hará que la contrapresión en el escritor reduzca el uso innecesario de la memoria para el almacenamiento en búfer, lo que generalmente ralentiza el tráfico de red.

Hay algunas diferencias críticas entre las tuberías y los canales de Go. Las tuberías no son lo mismo que un canal de golang. Las tuberías tratan de pasar bytes mutables en lugar de canales de Golang que son para señalizar con referencias de memoria (incluidos los punteros). Finalmente, no hay una select equivalente con tuberías.

(Las tuberías utilizan Spans [ https://adamsitnik.com/Span/] , que han existido por un tiempo, pero ahora están optimizadas profundamente en .Net Core. Los Spans mejoran el rendimiento significativamente. solo de forma incremental, por lo que el uso de .Net Framework está perfectamente bien.)

Por lo tanto, las tuberías son un estándar incorporado que debería ayudar a reemplazar los canales de Golang en .Net, pero no son lo mismo, y habrá muchos casos en los que las tuberías no son la respuesta.

Implementaciones directas del canal de Golang

Debería tener cuidado (como con Golang) de que los mensajes pasados ​​a través de un canal .Net indican un cambio de propiedad sobre un objeto. Esto es algo que solo un programador puede rastrear y verificar, y si se equivoca, tendrá dos o más subprocesos que acceden a los datos sin sincronización.