有没有办法在.NET中实现Erlang风格的轻量级进程?
我发现了一些实现Erlang消息传递模型(actor模型)的项目。例如,Axum。但我没有发现轻量级进程的实现。我指的是在单个OS线程或OS进程的上下文中运行的多个进程。
答案 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 Duffy和Dino 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中重现。但是在操作系统级别上没有“过程中的过程”这样的东西。