2010-04-12 6 views
7

Gibt es eine Möglichkeit, Erlang-Stil leichte Prozesse in .NET zu implementieren?Erlang-Stil leichte Prozesse in .NET

Ich habe einige Projekte gefunden, die das Erlang-Messaging-Modell (Akteursmodell) implementieren. Zum Beispiel Axum. Aber ich fand nichts über leichte Prozesse Implementierung. Ich meine mehrere Prozesse, die im Kontext eines einzelnen OS-Threads oder OS-Prozesses ablaufen.

Antwort

10

Ich denke, der F # MailboxProcessor ist, was Sie für Alexey suchen. Mit dem MailboxProcessor können Sie Zehntausende von Agenten innerhalb eines .NET-Prozesses definieren, ähnlich wie Sie in Erlang Zehntausende leichter Prozesse generieren können.

Diese MSDN post von Don Syme ist eine gute Einführung.

Wenn Sie von einem Erlang-Hintergrund zu .NET kommen, denken Sie daran, dass Ihnen viele OTP-Goodies fehlen (Supervisoren, Standorttransparenz, Mnesia, ...).

+0

Nützlich, vorausgesetzt, das OP ist nicht auf eine bestimmte Sprache ausgerichtet. –

+0

Ist es nicht grundsätzlich sicher, irgendeinen Hinweis auf Axum als "Woohoo! Alles geht, Baby!" :) –

-2

Das ergibt keinen Sinn. "Mehrere Prozesse, die im Kontext eines einzelnen Betriebssystem-Threads oder Betriebssystemprozesses ausgeführt werden" sind logisch nicht eindeutig. Dies ist im Grunde eine logische Anwendungslevel-Sache - die Sie problemlos in .NET reproportieren können. Aber so etwas wie einen "Prozess in einem Prozess" gibt es auf Betriebssystemebene nicht.

+2

"Prozess in einem Prozess" - Win32 haben eine Fasern. – W55tKQbuRu28Q4xv

+2

Aber Fasern sind selbstgesteuert - THREADS (und machen in den meisten Fällen keinen Sinn). Ein Prozess ist so definiert, dass er eine Speicherbarriere hat, der eine Faser fehlt. Eine Faser ist also keineswegs ein Prozess innerhalb eines Prozesses. – TomTom

+2

Sie haben das Laufzeitdesign von Erlang offensichtlich nicht angeschaut. Ja, Sie können Erlang-ähnliche Prozesse auf Anwendungsebene implementieren, aber das ist wahrscheinlich kooperatives Multitasking, eine Technik, die ich seit meinen DOS/Win16 Tagen nicht mehr benutzt habe. Wenn Sie Unterstützung in der Laufzeitumgebung haben, können Sie diesen Effekt mit präemptivem Multitasking erzielen, aber es wäre etwas, das MS zu .NET hinzufügen müsste. –

7

Die CLR kann gehostet werden und stellt Mechanismen für den Host bereit, um seine eigene Aufgabenabstraktion zu implementieren. Theoretisch können sie Threads, Fasern, LWPs sein - alles, solange der Host die necessaryinterfaces implementiert.

Es ist etwas schwierig, es richtig zu machen. MS nahm eine Chance, um die CLR im SQL Server Fiber Mode zu hosten.

Im letzten Moment gab es einige Stress Bugs, also zogen sie den Stecker nach Joe Duffy und Dino Vhieland (der eine series about writing a custom CLR host that implements its own task abstraction - with fibers - on his blog lief).
Momentan fehlt noch ein paar Klempner - ICLRTask::SwitchOut() - und selbst wenn man das schafft, würden die Bugs, die MS bei der Stresstest-Iteration treffen, wahrscheinlich auch die abenteuerlustige Seele verfolgen.

Angenommen, alle Probleme sind irgendwie behoben, und die gesamte Laufzeit ist darauf vorbereitet, auf Fasern, LWPs oder was auch immer zu laufen. Es gibt immer noch das Problem von P/Invoke, das möglicherweise Blockierungsvorgänge aufrufen könnte. Diese Art der Planung ist ohne Kernel-Unterstützung schwer zu bewerkstelligen.

Dies wird in den 64-Bit-Versionen von Windows 7 und Windows 2008 Server R2 angesprochen. Es gibt jetzt User-Mode Scheduling, die Kontrolle zurück zu einem Benutzer-Modus - im Gegensatz zum Kernel-Modus - Scheduler kann, wenn ein Aufruf im Kernel blockiert. Diese geplanten Threads im Benutzermodus sind echte Threads mit einem eigenen TLS. Dies sind großartige Verbesserungen und lassen viele Probleme im Fasermodus verschwinden.

Jetzt, UMS is utilized in der Concurrency Runtime das ist available for C++ and is part of the C Runtime Library (CRT).
Letzteres bedeutet, dass Sie es mit Visual Studio 2010 aus der Box verwenden können.

+0

Geht etwas davon weg, wenn Sie seine Verwendung begrenzen zu F # oder einer anderen funktionalen Sprache, um einige der Schwierigkeiten mit einem Prozess Mist mit anderen Speicher zu vermeiden? –

+0

@Warren: Die Aufgabenverwaltung hier kümmert sich nicht wirklich darum, ein Thread/LWL/LWP-Schreiben in den Speicher eines anderen zu verhindern. Schließlich ist es beabsichtigt - Sie tun dies jedes Mal, wenn Sie ein Multithread-Programm starten - alle Threads greifen auf den Prozess zu und teilen ihn. –

+0

@Warren: ... mit dem gesagt, gemeinsame Zustand zu vermeiden oder zu minimieren ist eine der großen Möglichkeiten, Parallelität zu erhöhen. Es kann auch in C# gemacht werden - obwohl die Sprache es nicht erzwingt oder spezifisch fördert. –

4

Haben Sie sich retlang angesehen? Ich habe nur darüber gelesen, aber ich habe nichts damit gemacht, aber ...

1

Erlang-Prozess ist wie Running-Methode parallel, aber Erlang-Variable kann nur einmal gebunden werden, so ist es Thread sicher, die nicht in C# ist.

so brauchen Sie zwei Dinge in C#, Thread sicher und parallel.

C# hat System.Threading.Task, Sie können viele Aufgaben in VM ausführen. C# vm plant diese Aufgabe in verschiedenen Arbeitsthreads.

Aber Aufgabe ist nicht threadsicher, Sie müssen eine Klasse namens Actor machen und den Status privat in den Actor legen.

Der Actor verfügt über einen System.Threading.SynchronizationContext und viele asynchrone Methoden wie diesen.

class Actor { 
    public SynchronizationContext _context; 

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


    public async Task Method1() { 
     await _context; 

     doSomething(); 
    } 

} 

Wenn andere Schauspieler von der Asynchron-Methode in diesem Schauspieler nennen, wird es eine Aufgabe erstellen, und die Aufgabe von vm geplant wird.

Sie müssen auch einen erwarteten und Thread-sicheren SynchronizationContext implementieren.

Dies ist ein threadsicherer Kontext.

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; 
    } 
} 

machen SynchroniztionContext awaitable

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

Erwartenden für 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(){} 
} 

dann Sie eine Schauspieler-Klasse mit Aufgabe bekommen und sicher Threads, wie Prozess in erlang.