Облегченные процессы в стиле Erlang в .NET - PullRequest
6 голосов
/ 12 апреля 2010

Есть ли способ реализовать облегченные процессы в стиле Erlang в .NET?

Я нашел несколько проектов, в которых реализована модель обмена сообщениями Erlang (модель актеров). Например, Аксум . Но я ничего не нашел о легких процессах реализации. Я имею в виду несколько процессов, которые выполняются в контексте одного потока ОС или процесса ОС.

Ответы [ 5 ]

10 голосов
/ 24 ноября 2010

Я думаю, что F # MailboxProcessor - это то, что вы ищете, Алексей.Используя MailboxProcessor, вы можете определить десятки тысяч агентов в одном .NET-процессе так же, как вы можете породить десятки тысяч легких процессов в Erlang.

Это сообщение MSDN от Don Syme - отличное введение.

Если вы заходите в .NET из опыта Эрланга, имейте в виду, что вам не хватит многих положительных героев OTP (супервизоры, прозрачность местоположения, мнезия, ...).

7 голосов
/ 12 апреля 2010

CLR может быть размещен и предоставляет хосту механизмы для реализации собственной абстракции задачи. Теоретически, это могут быть потоки, волокна, LWP - что угодно, если хост реализует необходимый интерфейс .

Правильно понять это довольно сложно. MS воспользовалась этим, чтобы разместить CLR в режиме волокна SQL Server.

В последний момент произошли некоторые стрессовые ошибки, поэтому они потянули за него заглушку в соответствии с Джо Даффи и Дино Вьеланд (который провел серию о письме пользовательский хост CLR, который реализует собственную абстракцию задачи - с волокнами - в своем блоге ).
Прямо сейчас отсутствует какая-то сантехника - ICLRTask::SwitchOut() - и даже если кто-то справится с этим, те же ошибки, которые попали в MS на итерации стресс-теста, вероятно, будут преследовать и авантюрную душу.

Предположим на мгновение, что все проблемы каким-то образом решены, и вся среда выполнения подготовлена ​​для работы на волокнах, LWP, что угодно. По-прежнему существует проблема P / Invoke, которая потенциально может вызывать операции блокировки. Такое планирование сложно обойтись без поддержки ядра.

Эта проблема решается в 64-разрядных версиях Windows 7 и Windows 2008 Server R2. Теперь есть Планирование в пользовательском режиме , которое может вернуть управление пользовательскому режиму - в отличие от планировщика режима ядра, если вызов блокирует ядро. Кроме того, эти запланированные потоки пользовательского режима являются реальными потоками с собственным TLS. Это большие улучшения, которые устраняют многие головные боли в волоконно-оптическом режиме.

В настоящее время UMS используется в параллельной среде выполнения , которая доступна для C ++ и является частью библиотеки времени выполнения C (CRT) .
Последнее означает, что вы можете использовать его из коробки с Visual Studio 2010 .

4 голосов
/ 13 апреля 2010

Вы смотрели на retlang ? Я только об этом читал, но пока ничего с этим не делал ...

1 голос
/ 15 декабря 2016

Процесс Erlang похож на параллельный запуск метода, но переменную erlang можно связать только один раз, поэтому он безопасен для потоков, чего нет в c #.

так что вам нужны две вещи в c #, поточно-ориентированные и параллельные.

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

}

Когда другие актеры вызывают асинхронный метод в этом актере, он создает задачу, и задача будет запланирована 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);
    }
}

Ожидание для синхронизацииContext

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.

0 голосов
/ 12 апреля 2010

Это не имеет смысла. «Несколько процессов, которые выполняются в контексте одного потока ОС или процесса ОС», логически не дает результатов. Это в основном логическая вещь уровня приложения, которую вы легко можете воспроизвести в .NET. Но на уровне ОС не существует такого понятия, как «процесс внутри процесса».

...