ContinueWith不尊重的奥尔良单线程性质 [英] Orleans single threaded nature not respected by ContinueWith

查看:115
本文介绍了ContinueWith不尊重的奥尔良单线程性质的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码( https://github.com/avinash0161/OrleansExperiments/tree / c0155b4b0c8c1bfe60aea8624f2cc83a52853dc7 ):

// Client code
Console.WriteLine("Client making a call");
var hashGenerator = client.GetGrain<IGrainA>(0);
hashGenerator.Call_A_ToTemp();
await Task.Delay(1000);
hashGenerator.Call_B_ToTemp();

// GrainA code
public async Task Call_A_ToTemp()
{
   Console.WriteLine("Making call A to a fellow grain");
   IGrainB grain = this.GrainFactory.GetGrain<IGrainB>(1);

   grain.CallA().ContinueWith((t)=>
   {
     if(t.IsFaulted)
     {
       // Silo message timeout is 32s so t.IsFaulted is true
       Console.WriteLine("Task Faulted");
       Call_A_ToTemp();
     }
    });
}

public async Task Call_B_ToTemp()
{
   Console.WriteLine("Making call B to a fellow grain");
   IGrainB grain = this.GrainFactory.GetGrain<IGrainB>(1);
   await grain.CallB();
}

// GrainB code
public async Task CallA()
{
   Console.WriteLine("Call A came to GrainB");
   await Task.Delay(34000);  // more than timeout for the caller
}

public Task CallB()
{
   Console.WriteLine("Call B came to GrainB");
   return Task.CompletedTask;
}

此代码的输出为:

Client making a call
Making call A to a fellow grain
Call A came to GrainB
Making call B to a fellow grain
Task Faulted                       <---------------- This comes after Call_B_ToTemp executes
Making call A to a fellow grain

如我们所见,Call_B_ToTemp在Call_A_ToTemp完全执行之前执行(Call_A_ToTemp的ContinueWith部分稍后执行)。

As we can see, that Call_B_ToTemp executes before Call_A_ToTemp executes completely (ContinueWith part of Call_A_ToTemp is executed later). Is this expected and does it violate the single threaded nature of the grains?

当我替换Call_A_ToTemp()中的代码时,这是否是预期的并且违反了谷物的单线程性质?使用:

When I replaced the code in Call_A_ToTemp() with:

public async Task Call_A_ToTemp()
{
    Console.WriteLine("Making call A to a fellow grain");
    IGrainB grain = this.GrainFactory.GetGrain<IGrainB>(1);

    bool isSuccess = false;
    while (! isSuccess)
    {
       try
       {
          await grain.CallA();
          isSuccess = true;
       } catch(TimeoutException){
            Console.WriteLine("task faulted");
       }

    }
}

代码现在保留单线程性质,在执行Call_A_ToTemp()的所有 ContinueWith 部分之前,不会调用Call_B_ToTemp。控制台输出如下:

The code now preserves the single threaded nature and Call_B_ToTemp isn't called till all of ContinueWith part of Call_A_ToTemp() is executed. The console output is like:

Client making a call
Making call A to a fellow grain
Call A came to GrainB
Task Faulted                       
Making call A to a fellow grain

谁能解释一下?当存在 ContinueWith 时是否违反了单线程性质?

Can anyone please explain this? Is the single threaded nature violated when there is ContinueWith?

推荐答案

不违反单线程性质。项目中的编译警告使问题的根源清晰可见。特别是:此异步方法缺少 await运算符,将同步运行。考虑使用'await'运算符来等待非阻塞API调用,或使用'await Task.Run(...)'在后台线程上进行CPU绑定工作。

The single-threaded nature is not being violated. The compilation warnings in your project makes the source of the issue clear. In particular: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

方法 async Task Call_A_ToTemp()从不等待对晶粒B的调用。相反,它在发出调用后立即返回。因为 Call_A_ToTemp()返回的 Task 立即完成,所以允许对谷物执行另一个调用。一旦 grain.CallA()完成,继续操作( ContinueWith(...))将在谷物的 TaskScheduler 尽快(例如,当谷物正在等待另一个呼叫或处于空闲状态时)。

The method async Task Call_A_ToTemp() never awaits the call to grain B. Instead, it returns immediately after issuing the call. Because the Task returned by Call_A_ToTemp() is immediately completed, another call is allowed to execute on the grain. Once grain.CallA() completes, the continuation (ContinueWith(...)) will execute on the grain's TaskScheduler as soon as possible (eg, while the grain is awaiting another call or sitting idle).

如果等待调用,或者如果从方法中删除了 async ,并且代码更改为返回 grain.CallA()。ContinueWith(...) 调用,将观察到预期的行为。即,将代码更改为此将给您预期的结果:

Instead, if the call was awaited or if async was removed from the method and the code changed to return the grain.CallA().ContinueWith(...) call then the expected behavior will be observed. I.e, changing the code to this will give you the expected result:

// removed 'async' here, since we're not awaiting anything.
// using 'async' is preferred, but this is to demonstrate a point about
// using ContinueWith and un-awaited calls
public Task Call_A_ToTemp()
{
   Console.WriteLine("Making call A to a fellow grain");
   IGrainB grain = this.GrainFactory.GetGrain<IGrainB>(1);

   // Note the 'return' here
   return grain.CallA().ContinueWith((t)=>
   {
     if(t.IsFaulted)
     {
       // Silo message timeout is 32s so t.IsFaulted is true
       Console.WriteLine("Task Faulted");
       Call_A_ToTemp();
     }
    });
}

这篇关于ContinueWith不尊重的奥尔良单线程性质的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆