.NET中的Erlang风格轻量级进程

时间:2010-04-12 08:40:19

标签: c# .net concurrency erlang lightweight-processes

有没有办法在.NET中实现Erlang风格的轻量级进程?

我发现了一些实现Erlang消息传递模型(actor模型)的项目。例如,Axum。但我没有发现轻量级进程的实现。我指的是在单个OS线程或OS进程的上下文中运行的多个进程。

5 个答案:

答案 0 :(得分:10)

我认为F#MailboxProcessor是您正在寻找的Alexey。使用MailboxProcessor,您可以在一个.NET进程中定义数万个代理,就像您可以在Erlang中生成数万个轻量级进程一样。

Don Syme的MSDN post是一个很好的介绍。

如果您从Erlang背景来到.NET,请记住,您将丢失许多OTP好东西(主管,位置透明度,mnesia ......)。

答案 1 :(得分:7)

可以托管CLR并公开主机实现其自己的任务抽象的机制。从理论上讲,它们可以是线程,光纤,LWP - 只要主机实现necessary interfaces

正确行事有点困难。为了在SQL Server光纤模式下托管CLR,MS抓住了机会。

在最后一刻,有一些压力错误,所以他们根据Joe DuffyDino Vhieland(谁跑了series about writing a custom CLR host that implements its own task abstraction - with fibers - on his blog)拔掉了插头。
现在有一些管道缺失 - ICLRTask::SwitchOut() - 即使有人绕过它,在压力测试迭代中遇到MS的同样的错误也可能会困扰冒险的灵魂。

假设所有问题都以某种方式得到解决,并且整个运行时准备在光纤,LWP上运行,无论如何。仍然存在P / Invoke可能会调用阻塞操作的问题。没有内核支持,这种调度很难做到。

这在64位版本的Windows 7和Windows 2008 Server R2中得到了解决。现在有User-Mode Scheduling可以控制回用户模式 - 而不是内核模式 - 调度程序,如果内核中的调用阻塞。此外,这些用户模式调度线程是具有自己的TLS的真实线程。这些都是很大的改进,并使许多纤维模式头痛消失。

现在,UMS is utilized Concurrency Runtime available for C++ and is part of the C Runtime Library (CRT)。{。} 后者意味着您可以使用Visual Studio 2010开箱即用。

答案 2 :(得分:4)

你看过retlang了吗?我只读过它,但我没有做任何事情,但是......

答案 3 :(得分:1)

Erlang进程就像并行运行方法一样,但是erlang变量只能被绑定一次,所以它是线程安全的,不在c#中。

所以你需要c#中的两件事,线程安全和并行。

C#有System.Threading.Task,你可以在vm中运行很多任务。 C#vm将在不同的工作线程中调度这些任务。

但是任务不是线程安全的,你需要创建一个名为Actor的类,并将状态置于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();
  }

}

当其他Actors在此Actor中调用异步方法时,它将创建一个任务,任务将由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的Awaiter

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(){}
} 

然后你得到一个带有任务和线程安全的Actor类,就像在erlang中处理一样。

答案 4 :(得分:-2)

这没有任何意义。 “在单个OS线程或OS进程的上下文中运行的多个进程”在逻辑上是不确定的。这基本上是一个逻辑应用程序级别的东西 - 你可以轻松地在.NET中重现。但是在操作系统级别上没有“过程中的过程”这样的东西。