私の同僚のいくつかの非同期コードを開発しているうちに、コードの一部で奇妙な問題が発生しました。コンポーネントは継続的なレートでトリガされましたが、一度に複数のタスクを実行することは絶対にありませんでした。同期モードに自動ダウングレードせずに非同期メソッドを呼び出す方法は?
私たちはタスクをキャッシュすることにしましたが、問題はありません。私たちは、キャッシュされたタスクの割り当てを回避し、完了したシングルトンタスクを削除しました。
Task onlyOneTask;
public Task TryToBeginSomeProcess()
{
lock (someLock)
{
if (onlyOneTask == null)
{
onlyOneTask = DoSomethingButOnlyOneAtATime();
}
return onlyOneTask;
}
}
public async Task DoSomethingButOnlyOneAtATime()
{
// do some work
await SomeWork();
lock (someLock)
{
onlyOneTask = null;
}
}
これは実際にはうまくいくようです... SomeWork()コールの中の何かが実際に生じると仮定します。ただし、次のような作業がある場合:
public Task SomeWork()
{
return Task.FromResult(0);
}
失敗します。しかし、それほど魅力的ではありません。 onlyOneTaskをクリアする行は、タスクが割り当てられる前に実行されます!その後では、onlyOneTaskに常にタスクインスタンスがあり、決してその作業を再実行しません。
デッドロックが発生すると考えられますが、threads may reacquire locksと思うかもしれません。非同期コードが完全同期コードにダウングレードする可能性があるため、同じスレッドで2番目のロックが取得されます。デッドロックはありません。
SomeWorkが実行中のある時点で発生すると、期待どおりの動作をしているように見えますが、正確な動作についていくつか質問があります。私は非同期コードが非同期に同期して実行されるときにスレッドとロックがどのように相互作用するかについて正確にはわかりません。
私が指摘したいことは、Task.Runを使用して作業を強制的に実行して独自のタスクを実行するように見えるということです。これには大きな問題があります。このコードは.NETサービスで実行するためのもので、it's bad form to use Task.Run thereです。
これを一見して修正する方法の1つは、非同期実装のようにタスクを常に動作させる何らかの方法がある場合です。私はそれが存在しないと思っていますが、私はよく尋ねるかもしれません。
ので、質問の提示へ:
- でき、このシナリオのデッドロックさえSomeWork()収量を場合は?
- TaskSchedulerが同じスレッドで継続してSomeWork()が十分に速い場合、非同期のケースは同期ケース(set only前にclearOneTask)のように動作できますか?
- スレッドプールを妥協することなく非同期であるかのように既存のタスクを常に動作させる方法はありますか?
- もっと良い選択肢はありますか?それは私達がちょうどSomeWorkを(開始するのが適切だったかを決定するために、キャッシュされたタスクの状態を検査した場合、私たちが発見価値がある何のため
)、再び私たちの問題は消えたので、これは自然の中で主に学術的です。
「いつものように動作するタスクを強制的にいくつかの方法非同期の実装を持っていました ":あなたは' 'Task.Yield()'を待ちましたか? ( 'await SomeWork();'の直前) – StriplingWarrior
これまでのところ、私はそうではありませんでした。 –