Облегченные процессы в стиле Erlang в.NET
Есть ли способ реализовать облегченные процессы в стиле Erlang в.NET?
Я нашел несколько проектов, которые реализуют модель сообщений Erlang (модель актеров). Например, Аксум. Но я ничего не нашел о реализации легких процессов. Я имею в виду несколько процессов, которые выполняются в контексте одного потока ОС или процесса ОС.
5 ответов
Я думаю, что F# MailboxProcessor - это то, что вы ищете Алексея. Используя MailboxProcessor, вы можете определить десятки тысяч агентов в одном.NET-процессе так же, как вы можете создавать десятки тысяч легких процессов в Erlang.
Этот пост MSDN Дона Сайма - отличное введение.
Если вы переходите на.NET из опыта Erlang, имейте в виду, что вам будет не хватать много вкусностей OTP (супервизоры, прозрачность местоположения, мнезия, ...).
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.
Вы смотрели на ретланга? Я только читал об этом, но я ничего не делал с этим, пока...
Процесс Erlang похож на параллельный запуск метода, но переменную erlang можно связать только один раз, поэтому он безопасен для потоков, чего нет в C#.
так что вам нужно две вещи в C#, потокобезопасные и параллельные.
C# имеет System.Threading.Task, вы можете запустить много задач в VM. C# vm будет планировать эти задачи в разных рабочих потоках.
Но задача не является поточно-ориентированной, вам нужно создать класс с именем Actor и поместить состояние private в 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.
Это не имеет никакого смысла. "Несколько процессов, которые выполняются в контексте одного потока ОС или процесса ОС", логически не дает результатов. Это в основном логическая вещь уровня приложения - которую вы можете легко воспроизвести в.NET. Но не существует такого понятия, как "процесс внутри процесса" на уровне ОС.