Однопоточная природа Орлеана не соблюдается ContinueWith - PullRequest
0 голосов
/ 31 января 2019

У меня есть следующий код (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 выполняется полностью (ContinueWith части Call_A_ToTemp выполняется позже.) Ожидается ли это и нарушает ли он однопоточный характер зерен?


Когда я заменил код в Call_A_ToTemp () на:

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_B_ToTemp не вызывается до тех пор, пока не будет выполнена вся ContinueWith часть Call_A_ToTemp (). Вывод консоли выглядит следующим образом:

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?

1 Ответ

0 голосов
/ 31 января 2019

Однопоточный характер не нарушается.Предупреждения компиляции в вашем проекте проясняют источник проблемы.В частности: 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. Вместо этого он возвращается сразу после вызова.Поскольку Task, возвращаемое Call_A_ToTemp(), немедленно завершается, разрешается выполнить еще один вызов в зерне.Как только grain.CallA() завершится, продолжение (ContinueWith(...)) будет выполнено для TaskScheduler зерна как можно скорее (например, пока зерно ожидает другого вызова или бездействует).

Вместо этого, еслиожидание вызова или если async был удален из метода, а код был изменен для возврата вызова grain.CallA().ContinueWith(...), то будет наблюдаться ожидаемое поведение.Т.е. изменение кода на это даст вам ожидаемый результат:

// 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();
     }
    });
}
...