.NET で Erlang スタイルの軽量プロセスを実装する方法はありますか?
Erlang メッセージング モデル (アクター モデル) を実装するプロジェクトをいくつか見つけました。たとえば、アクスム。しかし、軽量プロセスの実装については何も見つかりませんでした。単一の OS スレッドまたは OS プロセスのコンテキストで実行される複数のプロセスを意味します。
.NET で Erlang スタイルの軽量プロセスを実装する方法はありますか?
Erlang メッセージング モデル (アクター モデル) を実装するプロジェクトをいくつか見つけました。たとえば、アクスム。しかし、軽量プロセスの実装については何も見つかりませんでした。単一の OS スレッドまたは OS プロセスのコンテキストで実行される複数のプロセスを意味します。
F# MailboxProcessor は、Alexey が探しているものだと思います。MailboxProcessor を使用すると、Erlang で数万の軽量プロセスを生成できるように、1 つの .NET プロセス内で数万のエージェントを定義できます。
Don Syme によるこのMSDN の投稿は素晴らしい紹介です。
Erlang のバックグラウンドから .NET を使用している場合は、多くの OTP 機能 (スーパーバイザー、場所の透過性、記憶喪失など) を見逃していることを覚えておいてください。
CLRはホストでき、ホストが独自のタスク抽象化を実装するためのメカニズムを公開します。理論的には、スレッド、ファイバー、LWPなど、ホストが必要な インターフェイスを実装している限り、何でもかまいません。
それを正しくするのは少し難しいです。MSは、SQLServerファイバーモードでCLRをホストするためにチャンスをつかみました。
最後の瞬間、いくつかのストレスバグがあったので、JoeDuffyとDinoVhieland(彼のブログで独自のタスク抽象化を実装するカスタムCLRホストの作成に関するシリーズを実行した)によると、彼らはプラグを抜きました) 。
現在、いくつかの配管が欠落しています---ICLRTask::SwitchOut()
そしてそれを回避したとしても、ストレステストの反復でMSを襲った同じバグは、おそらく冒険心にも悩まされるでしょう。
しばらくの間、すべての問題が何らかの形で修正され、ランタイム全体がファイバー、LWPなどで実行できるように準備されているとします。ブロッキング操作を呼び出す可能性のあるP/Invokeの問題がまだあります。この種のスケジューリングは、カーネルのサポートなしでは実行できません。
これは、64ビットバージョンのWindows7およびWindows2008ServerR2で対処されています。呼び出しがカーネルでブロックされた場合に、カーネルモードではなくユーザーモードスケジューラに制御を戻すことができるユーザーモードスケジューリングがあります。また、これらのユーザーモードのスケジュールされたスレッドは、独自のTLSを持つ実際のスレッドです。これらは大きな改善であり、ファイバーモードの頭痛の種の多くを解消します。
現在、UMSは、C ++で使用可能で、Cランタイムライブラリ(CRT)の一部である同時実行ランタイムで使用されています。後者は、 VisualStudio2010
でそのまま使用できることを意味します。
retlangを見たことがありますか?私はそれについて読んだだけで、まだ何もしていません...
Erlang プロセスはメソッドを並列に実行するようなものですが、erlang 変数は 1 回しかバインドできないため、C# にはないスレッド セーフです。
そのため、C# では、スレッド セーフと並列の 2 つが必要です。
C# には System.Threading.Task があり、vm で多くのタスクを実行できます。C# vm は、これらのタスクを異なる作業スレッドでスケジュールします。
ただし、タスクはスレッド セーフではありません。Actor というクラスを作成し、状態を Actor に非公開にする必要があります。
アクターには、System.Threading.SynchronizationContext と、このような多くの非同期メソッドがあります。
class Actor {
public SynchronizationContext _context;
private int value1;
private Dictionary<> xxx;
private List<> xxx;
public async Task Method1() {
await _context;
doSomething();
}
}
他のアクタがこのアクタで async メソッドを呼び出すと、タスクが作成され、そのタスクは vm によってスケジュールされます。
また、待機可能でスレッドセーフな SynchronizationContext を実装する必要があります。
これはスレッドセーフなコンテキストです。
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;
}
}
SynchroniztionContext を待機可能にする
public static class SynchroniztionContextExtensions
{
public static SynchronizationContextAwaiter GetAwaiter (this SynchronizationContext context)
{
if(context == null) throw new ArgumentNullException("context");
return new SynchronizationContextAwaiter(context);
}
}
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(){}
}
次に、erlang のプロセスと同様に、タスクとスレッドセーフを備えた Actor クラスを取得します。
これは意味がありません。「単一の OS スレッドまたは OS プロセスのコンテキストで実行される複数のプロセス」は論理的に決定的ではありません。これは基本的に論理的なアプリケーション レベルのものであり、.NET で簡単に再現できます。しかし、OS レベルでは「プロセス内のプロセス」のようなものはありません。