適用 - 非同期 c#
「この非同期メソッドには「await」演算子がなく、同期的に実行される」という警告を心配する必要があります (4)
非同期メソッドを公開するインターフェイスがあります。 より具体的には、TaskまたはTask <T>を返すメソッドが定義されています。 async / awaitキーワードを使用しています。
私はこのインターフェースを実装する過程にいます。 ただし、これらのメソッドの一部では、この実装には何も待つ必要がありません。 そのため、「この非同期メソッドには「await」演算子がなく、同期して実行されます...」というコンパイラー警告が表示されます。
私はなぜエラーを受け取っているのか理解していますが、この文脈でそれらについて何かをすべきかどうか疑問に思っています。 コンパイラの警告を無視するのは間違っていると感じています。
Task.Runを待つことで修正できることはわかっていますが、安価な操作を数回しか実行していないメソッドにとっては間違っているように感じます。 また、実行に不要なオーバーヘッドが追加されるように聞こえますが、asyncキーワードが存在するため、既に存在するかどうかもわかりません。
警告を無視する必要がありますか、それともこの問題を回避する方法がありますか?
Michael Liuは、Task.FromResultを返すことで、警告を回避する方法に関する質問によく答えました。
あなたの質問の「警告について心配する必要があります」の部分に答えます。
答えはイエスです!
これは、
await
演算子を使用せずに非同期メソッド内で
Task
を返すメソッドを呼び出すと、警告が頻繁に発生する
await
です。
前の操作を待たずにEntity Frameworkで操作を呼び出したために発生した同時実行バグを修正しました。
コンパイラの警告を回避するために細心の注意を払ってコードを記述できる場合、警告があると、ひどい親指のように目立ちます。 数時間のデバッグを回避できたはずです。
この警告を回避するためのトリックを見つけました。
public async Task<object> test()
{
//a pseudo code just to disable the warning about lack of await in async code!
var xyz = true ? 0 : await Task.FromResult(0); //use a var name that's not used later
//... your code statements as normal, eg:
//throw new NotImplementedException();
}
その
await
キーワードの存在は、たとえ呼び出されないとわかっていても、警告を発しないようにコンパイラを詐欺します!
条件が
true
ため、常に三項条件(?:)の最初の部分を返します。また、この変数は使用しない変数なので、リリースビルドでは省略されます。
このアプローチに副作用があるかどうかはわかりません。
遅すぎるかもしれませんが、役に立つ調査かもしれません:
コンパイル済みコード( IL )の内部構造については次のとおりです。
public static async Task<int> GetTestData()
{
return 12;
}
ILになります:
.method private hidebysig static class [mscorlib]System.Threading.Tasks.Task`1<int32>
GetTestData() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( 01 00 28 55 73 61 67 65 4C 69 62 72 61 72 79 2E // ..(UsageLibrary.
53 74 61 72 74 54 79 70 65 2B 3C 47 65 74 54 65 // StartType+<GetTe
73 74 44 61 74 61 3E 64 5F 5F 31 00 00 ) // stData>d__1..
.custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 )
// Code size 52 (0x34)
.maxstack 2
.locals init ([0] class UsageLibrary.StartType/'<GetTestData>d__1' V_0,
[1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> V_1)
IL_0000: newobj instance void UsageLibrary.StartType/'<GetTestData>d__1'::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create()
IL_000c: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder'
IL_0011: ldloc.0
IL_0012: ldc.i4.m1
IL_0013: stfld int32 UsageLibrary.StartType/'<GetTestData>d__1'::'<>1__state'
IL_0018: ldloc.0
IL_0019: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder'
IL_001e: stloc.1
IL_001f: ldloca.s V_1
IL_0021: ldloca.s V_0
IL_0023: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<class UsageLibrary.StartType/'<GetTestData>d__1'>(!!0&)
IL_0028: ldloc.0
IL_0029: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder'
IL_002e: call instance class [mscorlib]System.Threading.Tasks.Task`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task()
IL_0033: ret
} // end of method StartType::GetTestData
そして、非同期およびタスクメソッドなし:
public static int GetTestData()
{
return 12;
}
になります:
.method private hidebysig static int32 GetTestData() cil managed
{
// Code size 8 (0x8)
.maxstack 1
.locals init ([0] int32 V_0)
IL_0000: nop
IL_0001: ldc.i4.s 12
IL_0003: stloc.0
IL_0004: br.s IL_0006
IL_0006: ldloc.0
IL_0007: ret
} // end of method StartType::GetTestData
これらの方法の大きな違いを見ることができるように。 asyncメソッド内でawaitを使用せず、asyncメソッド(API呼び出しやイベントハンドラーなど)の使用を気にしない場合は、通常のsyncメソッドに変換することをお勧めします(アプリケーションのパフォーマンスを節約します)。
async キーワードは、メソッドの実装の詳細にすぎません。 メソッドシグネチャの一部ではありません。 特定のメソッドの実装またはオーバーライドに Task.FromResult<TResult> するものがない場合は、 async キーワードを省略し、 Task.FromResult<TResult> を使用して完了したタスクを Task.FromResult<TResult> ます。
public Task<string> Foo() // public async Task<string> Foo()
{ // {
Baz(); // Baz();
return Task.FromResult("Hello"); // return "Hello";
} // }
メソッドが
Task
Task<TResult>
ではなく
Task
を返す場合、任意のタイプと値の完了したタスクを返すことができます。
Task.FromResult(0)
は一般的な選択肢のようです:
public Task Bar() // public async Task Bar()
{ // {
Baz(); // Baz();
return Task.FromResult(0); //
} // }
または、.NET Framework 4.6以降、 Task.CompletedTask を返すことができます。
public Task Bar() // public async Task Bar()
{ // {
Baz(); // Baz();
return Task.CompletedTask; //
} // }