2010-04-12 11 views
7

¿Hay alguna manera de implementar procesos livianos de estilo Erlang en .NET?Procesos livianos de estilo Erlang en .NET

Encontré algunos proyectos que implementan el modelo de mensajería de Erlang (modelo de actores). Por ejemplo, Axum. Pero no encontré nada sobre procesos ligeros implementación. Me refiero a múltiples procesos que se ejecutan en un contexto de un único subproceso del sistema operativo o proceso del sistema operativo.

Respuesta

10

Creo que el F # MailboxProcessor es lo que estás buscando Alexey. Usando el MailboxProcessor puede definir decenas de miles de agentes en un proceso .NET de forma similar a como puede engendrar decenas de miles de procesos livianos en Erlang.

Este MSDN post de Don Syme es una gran introducción.

Si vienes a .NET desde un fondo de Erlang ten en cuenta que te faltarán muchos objetos OTP (supervisores, transparencia de ubicación, mnesia, ...).

+0

Útil, suponiendo que el OP no está enfocado en un idioma en particular. –

+0

¿No es generalmente seguro interpretar cualquier referencia a Axum como "¡Woohoo! ¡Todo vale, cariño!" :) –

-2

Esto no tiene sentido. "Múltiples procesos que se ejecutan en el contexto de un único proceso del sistema operativo o subproceso del sistema operativo" no es lógicamente concluyente. Esto es básicamente una cosa lógica de nivel de aplicación, que puedes repro fácilmente en .NET. Pero no hay tal cosa como un "proceso dentro de un proceso" en el nivel del sistema operativo.

+2

"proceso dentro de un proceso" - Win32 tiene fibras. – W55tKQbuRu28Q4xv

+2

Pero las fibras son autoprogramadas - HILOS (y no tienen sentido en la MAYORÍA de los casos). Un proceso se define como tener una barrera de memoria, que carece de fibra. Entonces, una fibra no es un proceso dentro de un proceso. – TomTom

+2

Es evidente que no ha mirado el diseño de tiempo de ejecución de Erlang. Sí, puede implementar procesos similares a Erlang en el nivel de la aplicación, pero es probable que se convierta en una multitarea cooperativa, una técnica que no he visto desde mi DOS/Win16 días. Si tiene soporte en el tiempo de ejecución, puede obtener este efecto con la multitarea preventiva, pero sería algo que MS tendría que agregar a .NET. –

7

El CLR se puede alojar y expone los mecanismos para que el host implemente su propia abstracción de tareas. Teóricamente, pueden ser hilos, fibras, LWP, cualquier cosa mientras el host implemente el necessaryinterfaces.

Hacer las cosas bien es algo difícil. MS se arriesgó para alojar el CLR en el modo de fibra de SQL Server.

En el último momento, hubo algunos errores de estrés, por lo que sacó el tapón en él de acuerdo con Joe Duffy y Dino Vhieland (que dirigía un series about writing a custom CLR host that implements its own task abstraction - with fibers - on his blog).
En este momento falta algo de fontanería - ICLRTask::SwitchOut() - e incluso si uno se da cuenta de eso, los mismos errores que afectan a la EM en la iteración de la prueba de estrés probablemente también acecharían al alma aventurera.

Supongamos por un momento que todos los problemas se arreglan de alguna manera y todo el tiempo de ejecución está preparado para funcionar con fibras, LWP, lo que sea. Todavía existe el problema de P/Invoke que podría llamar operaciones de bloqueo. Este tipo de programación es difícil de hacer sin compatibilidad con kernel.

Esto se trata en las versiones de 64 bits de Windows 7 y Windows 2008 Server R2. Ahora hay User-Mode Scheduling que puede devolver el control a un modo de usuario - a diferencia del planificador kernel-mode si una llamada se bloquea en el kernel. Además, estos subprocesos programados en modo de usuario son subprocesos reales con su propio TLS. Estas son grandes mejoras y hacen desaparecer muchos de los dolores de cabeza del modo de fibra.

En este momento, UMS is utilized en el Concurrency Runtime que es available for C++ and is part of the C Runtime Library (CRT).
Esto último significa que puede usarlo de fábrica con Visual Studio 2010.

+0

Hace que algo de esto desaparezca si limita su uso a F # u otro lenguaje funcional, para evitar algunas de las dificultades con un proceso de limpieza con el almacenamiento de otro? –

+0

@Warren: la gestión de tareas aquí realmente no se ocupa de prevenir la escritura de un hilo/fibra/LWP en la memoria de otro. Después de todo, se trata de un comportamiento intencionado; esto se hace cada vez que se inicia un programa de subprocesos múltiples; todos los subprocesos acceden y comparten la memoria del proceso. –

+0

@Warren: ... Dicho esto, evitar o minimizar el estado compartido es una de las mejores maneras de aumentar el paralelismo. También se puede hacer en C#, aunque el lenguaje no lo hace cumplir o lo alienta específicamente. –

4

¿Has echado un vistazo a retlang? Solo lo leí, pero no hice nada con eso, sin embargo ...

1

El proceso de Erlang es como ejecutar el método en paralelo, pero la variable erlang solo se puede vincular una vez, por lo que es segura para subprocesos y no está en C#.

por lo que necesita dos cosas en C#, hilo seguro y paralelo.

C# tiene System.Threading.Task, puede ejecutar muchas tareas en vm. C# vm programará estas tareas en diferentes hilos de trabajo.

Pero la tarea no es segura para subprocesos, debe crear una clase llamada Actor y poner el estado privado en el Actor.

El Actor tiene un System.Threading.SynchronizationContext, y muchos métodos async, como este.

class Actor { 
    public SynchronizationContext _context; 

    private int value1; 
    private Dictionary<> xxx; 
    private List<> xxx; 


    public async Task Method1() { 
     await _context; 

     doSomething(); 
    } 

} 

Cuando otros actores llaman al método asíncrono en este actor, se creará una tarea, y la tarea será programada por vm.

También necesita implementar un SynchronizationContext que sea seguro y esté seguro para la ejecución de subprocesos.

este es un contexto seguro para las subunidades.

public class ActorSynchronizationContext : SynchronizationContext 
{ 
    private readonly SynchronizationContext _subContext; 
    private readonly ConcurrentQueue<Action> _pending = new ConcurrentQueue<Action>(); 
    private int _pendingCount; 

    public ActorSynchronizationContext(SynchronizationContext context = null) 
    { 
     this._subContext = context ?? new SynchronizationContext(); 
    } 

    public override void Post(SendOrPostCallback d, object state) 
    { 
     if (d == null) { 
      throw new ArgumentNullException("SendOrPostCallback"); 
     } 
     _pending.Enqueue(() => d(state)); 
     if (Interlocked.Increment(ref _pendingCount) == 1) 
     { 
      try 
      { 
       _subContext.Post(Consume, null); 
      } 
      catch (Exception exp) 
      { 
       LogHelper.LogUnhandleException(exp.ToString()); 
      } 
     } 
    } 

    private void Consume(object state) 
    { 
     var surroundContext = Current; 
     SetSynchronizationContext(this); 
     do 
     { 
      Action a; 
      _pending.TryDequeue(out a); 
      try 
      { 
       a.Invoke(); 
      } 
      catch (Exception exp) 
      { 
       //Debug.LogError(exp.ToString()); 
       LogHelper.LogUnhandleException(exp.ToString()); 
      } 
     } while (Interlocked.Decrement(ref _pendingCount) > 0); 
     SetSynchronizationContext(surroundContext); 
    } 

    public override void Send(SendOrPostCallback d, object state) 
    { 
     throw new NotSupportedException(); 
    } 
    public override SynchronizationContext CreateCopy() 
    { 
     return this; 
    } 
} 

hacen SynchroniztionContext awaitable

public static class SynchroniztionContextExtensions 
{ 
    public static SynchronizationContextAwaiter GetAwaiter (this SynchronizationContext context) 
    { 
     if(context == null) throw new ArgumentNullException("context"); 
     return new SynchronizationContextAwaiter(context); 
    } 
} 

Awaiter para SynchronizationContext

public sealed class SynchronizationContextAwaiter : INotifyCompletion 
{ 
    private readonly SynchronizationContext _context; 
    public SynchronizationContextAwaiter(SynchronizationContext context) 
    { 
     if(context == null) throw new ArgumentNullException("context"); 
     _context = context; 
    } 
    public bool IsCompleted { 
     get 
     { 
      //已经在当前上下文里面了,就不需要再次切换上下文 
      return SynchronizationContext.Current == _context; 
     } 
    } 

    /// <summary> 
    /// 将Action 任务调度到 _context 控制的线程里面去执行 
    /// 
    /// var temp = e.GetAwaiter(); 
    /// </summary> 
    /// <param name="action">Action.</param> 
    public void OnCompleted(Action action) { 
     _context.Post(x=>action(), null); 
    } 
    public void GetResult(){} 
} 

continuación, se obtiene una clase de actor con la tarea y el hilo de seguridad que al igual que el proceso de Erlang.

Cuestiones relacionadas