返される集約オブジェクトを構築するコントローラーを備えた.netコアAPIがあります。作成するオブジェクトは、サービスクラスへの3つのメソッド呼び出しからのデータで構成されます。これらはすべて互いに独立しており、互いに分離して実行できます。現在、このコントローラーのパフォーマンスを向上させるためにタスクを使用しています。現在のバージョンは次のようになります...
[HttpGet]
public IActionResult myControllerAction()
{
var data1 = new sometype1();
var data2 = new sometype2();
var data3 = new List<sometype3>();
var t1 = new Task(() => { data1 = service.getdata1(); });
t1.Start();
var t2 = new Task(() => { data2 = service.getdata2(); });
t2.Start();
var t3 = new Task(() => { data3 = service.getdata2(); });
t3.Start();
Task.WaitAll(t1, t2, t3);
var data = new returnObject
{
d1 = data1,
d2 = data2,
d2 = data3
};
return Ok(data);
}
これはうまく機能しますが、ここでタスクを使用することが最善の解決策であるかどうか疑問に思っていますか? async/awaitを使用することは、より良いアイデアであり、より受け入れられた方法でしょうか?
たとえば、コントローラを非同期としてマークし、サービスメソッドの呼び出しごとに待機を設定する必要がありますか?
これはうまく機能しますが、ここでタスクを使用することが最善の解決策であるかどうか疑問に思っていますか? async/awaitを使用することは、より良いアイデアであり、より受け入れられた方法でしょうか?
そのとおり。 ASP.NETで並列処理を実行すると、要求ごとに複数のスレッドが消費され、スケーラビリティに大きな影響を与える可能性があります。非同期処理は、I/Oに対してはるかに優れています。
async
を使用するには、まずサービス内の最低レベルの呼び出しから始めます。おそらく、ある時点でHTTP呼び出しを行っているのでしょう。非同期HTTP呼び出しを使用するように変更します(例:HttpClient
)。その後、async
をそこから自然に成長させます。
最終的には、非同期のgetdata1Async
、getdata2Async
、およびgetdata3Async
メソッドになりますが、これらは同時に使用できます。
[HttpGet]
public async Task<IActionResult> myControllerAction()
{
var t1 = service.getdata1Async();
var t2 = service.getdata2Async();
var t3 = service.getdata3Async();
await Task.WhenAll(t1, t2, t3);
var data = new returnObject
{
d1 = await t1,
d2 = await t2,
d3 = await t3
};
return Ok(data);
}
このアプローチでは、3つのサービス呼び出しが進行中に、myControllerAction
はfourではなくzeroスレッドを使用します。
[HttpGet]
public async Task<IActionResult> GetAsync()
{
var t1 = Task.Run(() => service.getdata1());
var t2 = Task.Run(() => service.getdata2());
var t3 = Task.Run(() => service.getdata3());
await Task.WhenAll(t1, t2, t3);
var data = new returnObject
{
d1 = t1.Status == TaskStatus.RanToCompletion ? t1.Result : null,
d2 = t2.Status == TaskStatus.RanToCompletion ? t2.Result : null,
d3 = t3.Status == TaskStatus.RanToCompletion ? t3.Result : null
};
return Ok(data);
}
TaskWhenAll
を使用して、待機可能なTaskオブジェクトを返します。したがって、非同期メソッドを使用すると、スレッドをブロックする代わりにタスクを待つことができます。Task<T>
を使用して必要なタイプの結果を返すことができます。Task<TResult>.Run
メソッドを使用しますGet
で始まる必要がありますnull
値を使用しました。別のアプローチを使用できます-例えば一部のタスクが失敗した場合にエラーを返します。私が理解しているように、あなたはこれを並行して実行したいので、コードに何か問題があるとは思わない。ガブリエルが言ったように、タスクの完了を待つことができます。
[HttpGet]
public async Task<IActionResult> myControllerAction()
{
var data1 = new sometype1();
var data2 = new sometype2();
var data3 = new List<sometype3>();
var t1 = Task.Run(() => { data1 = service.getdata1(); });
var t2 = Task.Run(() => { data2 = service.getdata2(); });
var t3 = Task.Run(() => { data3 = service.getdata3(); });
await Task.WhenAll(t1, t2, t3); // otherwise a thread will be blocked here
var data = new returnObject
{
d1 = data1,
d2 = data2,
d2 = data3
};
return Ok(data);
}
また、タスクの結果を使用して、コードのいくつかの行を保存し、コード全体を「より良い」ものにすることもできます(コメントを参照)。
[HttpGet]
public async Task<IActionResult> myControllerAction()
{
var t1 = Task.Run(() => service.getdata1() );
var t2 = Task.Run(() => service.getdata2() );
var t3 = Task.Run(() => service.getdata3() );
await Task.WhenAll(t1, t2, t3); // otherwise a thread will be blocked here
var data = new returnObject
{
d1 = t1.Result,
d2 = t2.Result,
d2 = t3.Result
};
return Ok(data);
}