有没有办法在 .NET 中实现 Erlang 风格的轻量级进程?
我发现了一些实现 Erlang 消息传递模型(参与者模型)的项目。例如,阿克苏姆。但是我对轻量级流程的实现一无所知。我的意思是在单个操作系统线程或操作系统进程的上下文中运行的多个进程。
有没有办法在 .NET 中实现 Erlang 风格的轻量级进程?
我发现了一些实现 Erlang 消息传递模型(参与者模型)的项目。例如,阿克苏姆。但是我对轻量级流程的实现一无所知。我的意思是在单个操作系统线程或操作系统进程的上下文中运行的多个进程。
我认为 F# MailboxProcessor 是您正在寻找的 Alexey。使用 MailboxProcessor,您可以在一个 .NET 进程中定义数以万计的代理,就像您可以在 Erlang 中生成数以万计的轻量级进程一样。
Don Syme 的这篇MSDN 帖子是一个很好的介绍。
如果您是从 Erlang 背景开始使用 .NET,请记住,您将错过很多 OTP 好东西(主管、位置透明度、记忆力,...)。
CLR 可以被托管并为宿主公开机制以实现其自己的任务抽象。从理论上讲,它们可以是线程、纤程、LWP——只要主机实现了必要的 接口即可。
把它做好有点困难。MS 借此机会在 SQL Server 光纤模式下托管 CLR。
在最后一刻,出现了一些压力错误,因此根据Joe Duffy和Dino Vhieland的说法,他们拔掉了插头(他们在他的博客上运行了一系列关于编写自定义 CLR 主机来实现自己的任务抽象 - 使用纤维 -) .
现在有一些管道缺失--ICLRTask::SwitchOut()
即使解决了这个问题,在压力测试迭代中攻击 MS 的相同错误也可能会困扰冒险的灵魂。
假设所有问题都以某种方式得到解决,并且整个运行时都准备好在光纤、LWP 等上运行。仍然存在可能调用阻塞操作的 P/Invoke 问题。如果没有内核支持,这种调度是很难做到的。
这在 64 位版本的 Windows 7 和 Windows 2008 Server R2 中得到解决。现在有用户模式调度,如果内核中的调用阻塞,可以将控制权交还给用户模式- 与内核模式相反 - 调度程序。此外,这些用户模式调度线程是具有自己 TLS 的真实线程。这些都是很大的改进,使许多光纤模式的烦恼消失了。
目前,UMS在可用于 C++的并发运行时中使用,并且是 C 运行时库 (CRT) 的一部分。后者意味着您可以通过Visual Studio 2010
开箱即用地使用它。
你看过retlang吗?我只是读过它,但我没有对它做任何事情,然而......
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();
}
}
当其他 Actor 调用此 Actor 中的 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(){}
}
然后你会得到一个具有任务和线程安全的 Actor 类,它类似于 erlang 中的进程。
这是没有意义的。“在单个 OS 线程或 OS 进程的上下文中运行的多个进程”在逻辑上是不确定的。这基本上是一个逻辑应用程序级别的事情 - 您可以轻松地在 .NET 中重现。但是在操作系统级别上没有“进程中的进程”之类的东西。