我正在建立一个用于.NET的AKKA框架的端口(现在不要太认真了,现在这是一个周末黑客的演员部分)
我的“未来”支持存在一些问题。 在Java / Scala Akka中,期待与Await调用同步进行。 很像.NET Task.Wait()
我的目标是支持真正的异步等待。 它现在可以正常工作,但是在我当前的解决方案中,继续在错误的线程上执行。
这是将消息传递给我的一个包含未来await块的actor的结果。 如您所见,actor始终在同一个线程上执行,而await块在随机线程池线程上执行。
actor thread: 6
await thread 10
actor thread: 6
await thread 12
actor thread: 6
actor thread: 6
await thread 13
...
演员使用DataFlow BufferBlock<Message>
收到消息
或者更确切地说,我在缓冲区块上使用RX来订阅消息。
它的配置如下:
var messages = new BufferBlock<Message>()
{
BoundedCapacity = 100,
TaskScheduler = TaskScheduler.Default,
};
messages.AsObservable().Subscribe(this);
到目前为止一切顺利。
然而,当我等待未来的结果时。 像这样:
protected override void OnReceive(IMessage message)
{
....
var result = await Ask(logger, m);
// This is not executed on the same thread as the above code
result.Match()
.With<SomeMessage>(t => {
Console.WriteLine("await thread {0}",
System.Threading.Thread.CurrentThread.GetHashCode());
})
.Default(_ => Console.WriteLine("Unknown message"));
...
我知道这是异步等待的正常行为,但我必须确保只有一个线程可以访问我的actor。
我不希望未来同步运行,我想像正常一样运行异步,但我希望继续在与消息处理器/ actor相同的线程上运行。
我未来支持的代码如下所示:
public Task<IMessage> Ask(ActorRef actor, IMessage message)
{
TaskCompletionSource<IMessage> result =
new TaskCompletionSource<IMessage>();
var future = Context.ActorOf<FutureActor>(name : Guid.NewGuid().ToString());
// once this object gets a response,
// we set the result for the task completion source
var futureActorRef = new FutureActorRef(result);
future.Tell(new SetRespondTo(), futureActorRef);
actor.Tell(message, future);
return result.Task;
}
我可以做些什么来强制继续在启动上述代码的同一个线程上运行?
答案 0 :(得分:6)
我正在为.NET的
创建一个AKKA框架的端口
甜。尽管从未接触过Java / Scala / Akka,我去了CodeMash '13的Akka演讲。我看到了.NET库/框架的很多潜力。微软is working on something similar,我希望最终能够获得它(currently in a limited preview)。
我怀疑尽可能多地留在Dataflow / Rx世界是更容易的方法;当你有异步操作(每个操作只有一个启动和单个结果)时,async
是最好的,而数据流和Rx在流和订阅(使用单个启动和多个结果)时效果更好。所以我的第一个直觉反应是将缓冲区块与具有特定调度程序的ActionBlock
链接,或使用ObserveOn
将Rx通知移动到特定的调度程序,而不是尝试在{ {1}}方。当然我对Akka API的设计并不熟悉,所以请大家用它。
无论如何,我的async
intro描述了用于安排async
延续的唯一两个可靠选项:await
和SynchronizationContext.Current
。如果您的Akka端口更像是框架(您的代码执行托管,最终用户代码始终由您的代码执行),那么TaskScheduler.Current
可能有意义。如果您的端口更像是库(最终用户代码执行托管并根据需要调用您的代码),那么SynchronizationContext
会更有意义。
自定义TaskScheduler
的示例并不多,因为这种情况非常罕见。我的AsyncContextThread
type中有一个AsyncEx library,它为该帖子定义了SynchronizationContext
和SynchronizationContext
。有几个自定义TaskScheduler
的示例,例如Parallel Extensions Extras,其中包含STA scheduler和"current thread" scheduler。
答案 1 :(得分:0)
任务计划程序决定是在新线程上还是在当前线程上运行任务。 有一个选项强制在新线程上运行它,但没有强制它在当前线程上运行。 但是有一个方法Task.RunSynchronously(),它在当前的TaskScheduler上同步运行任务。 此外,如果你使用async / await,那就已经存在类似的问题了。