• Should I worry about "This async method lacks 'await' operators and will run synchronously" warning


    Should I worry about "This async method lacks 'await' operators and will run synchronously" warning

    问题

    I have a interface which exposes some async methods. More specifically it has methods defined which return either Task or Task<T>. I am using the async/await keywords.

    I am in the process of implementing this interface. However, in some of these methods this implementation doesn't have anything to await. For that reason I am getting the compiler warning "CS1998: This async method lacks 'await' operators and will run synchronously..."

    I understand why I am getting these warnings but am wondering whether I should do anything about them in this context. It feels wrong to ignore compiler warnings.

    I know I can fix it by awaiting on Task.Run but that feels wrong for a method that is only doing a few inexpensive operations. It also sounds like it will add unneeded overhead to the execution but then I am also not sure if that is already there because the async keyword is present.

    Should I just ignore the warnings or is there a way of working around this that I am not seeing?

    评论

    Just remove the async keyword. You can still return a Task using Task.FromResult. Apr 28, 2015 at 15:15

    回答1

    The async keyword is merely an implementation detail of a method; it isn’t part of the method signature. If a particular method implementation or override has nothing to await, then just omit the async keyword and return a completed task using Task.FromResult<TResult>:

    public Task<string> Foo()               //    public async Task<string> Foo()
    {                                       //    {
        Baz();                              //        Baz();
        return Task.FromResult("Hello");    //        return "Hello";
    }                                       //    }

    If your method return type is Task instead of Task<TResult>, then return Task.CompletedTask:

    public Task Bar()                       //    public async Task Bar()
    {                                       //    {
        Baz();                              //        Baz();
        return Task.CompletedTask;          //
    }                                       //    }

    Task.CompletedTask was added in .NET Framework 4.6. If you’re targeting an earlier version, then you can instead return a completed task of any type and value. Task.FromResult(0) seems to be a popular choice:

    public Task Bar()                       //    public async Task Bar()
    {                                       //    {
        Baz();                              //        Baz();
        return Task.FromResult(0);          //
    }                                       //    }

    Dealing with Exceptions

    An exception thrown by a non-async method propagates immediately up the call stack, but an exception thrown by an async method is stored in the returned Task object and propagates only when the Task is awaited. This makes a big difference if someone calls your method and then does something else before awaiting the Task:

    Task<string> task = Foo();   // If Foo is async and throws an exception,
    DoSomethingElse();           // then this line will be executed,
    string result = await task;  // and the exception will be rethrown here.

    If you need to preserve this behavior for a non-async method, then wrap the entire method within a try...catch statement. Pass any unhandled exception to Task.FromException, and return the result:

    public Task<string> Foo()                       //  public async Task<string> Foo()
    {                                               //  {
        try                                         //
        {                                           //
            Baz(); // might throw                   //      Baz();
            return Task.FromResult("Hello");        //      return "Hello";
        }                                           //
        catch (Exception ex)                        //
        {                                           //
            return Task.FromException<string>(ex);  //
        }                                           //
    }                                               //  }
    
    public Task Bar()                               //  public async Task Bar()
    {                                               //  {
        try                                         //
        {                                           //
            Baz(); // might throw                   //      Baz();
            return Task.CompletedTask;              //
        }                                           //
        catch (Exception ex)                        //
        {                                           //
            return Task.FromException(ex);          //
        }                                           //
    }                                               //  }

    The generic argument to Task.FromException must match the return type of the method.

     回答2

    It's perfectly reasonable that some "asynchronous" operations complete synchronously, yet still conform to the asynchronous call model for the sake of polymorphism.

    A real-world example of this is with the OS I/O APIs. Asynchronous and overlapped calls on some devices always complete inline (writing to a pipe implemented using shared memory, for example). But they implement the same interface as multi-part operations which do continue in the background.

    回答3

    Only if you are actually calling the method involved, and only if performance is a concern.

    This can be demonstrated by writing a program containing the following 4 methods, then decompiling them to IL (note that IL presented may change between runtime versions; the below is from .NET Core 3.1):

    int FullySync() => 42;
    
    Task<int> TaskSync() => Task.FromResult(42);
    
    // CS1998
    async Task<int> NotActuallyAsync() => 42;
    
    async Task<int> FullyAsync() => await Task.Run(() => 42);

    The first two result in very short method bodies containing exactly what you would expect:

    .method private hidebysig 
        instance int32 FullySync () cil managed 
    {
        // Method begins at RVA 0x205e
        // Code size 3 (0x3)
        .maxstack 8
    
        // return 42;
        IL_0000: ldc.i4.s 42
        IL_0002: ret
    } // end of method Program::FullySync
    
    .method private hidebysig 
        instance class [System.Runtime]System.Threading.Tasks.Task`1<int32> TaskSync () cil managed 
    {
        // Method begins at RVA 0x2062
        // Code size 8 (0x8)
        .maxstack 8
    
        // return Task.FromResult(42);
        IL_0000: ldc.i4.s 42
        IL_0002: call class [System.Runtime]System.Threading.Tasks.Task`1<!!0> [System.Runtime]System.Threading.Tasks.Task::FromResult<int32>(!!0)
        IL_0007: ret
    } // end of method Program::TaskSync

    But the presence of the async keyword on the last two causes the compiler to generate asynchronous state machines for those methods:

    .method private hidebysig 
        instance class [System.Runtime]System.Threading.Tasks.Task`1<int32> NotActuallyAsync () cil managed 
    {
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [System.Runtime]System.Type) = (
            01 00 29 43 53 31 39 39 38 54 65 73 74 2e 50 72
            6f 67 72 61 6d 2b 3c 4e 6f 74 41 63 74 75 61 6c
            6c 79 41 73 79 6e 63 3e 64 5f 5f 33 00 00
        )
        .custom instance void [System.Diagnostics.Debug]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = (
            01 00 00 00
        )
        // Method begins at RVA 0x206c
        // Code size 56 (0x38)
        .maxstack 2
        .locals init (
            [0] class CS1998Test.Program/'<NotActuallyAsync>d__3'
        )
    
        IL_0000: newobj instance void CS1998Test.Program/'<NotActuallyAsync>d__3'::.ctor()
        IL_0005: stloc.0
        IL_0006: ldloc.0
        IL_0007: call valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create()
        IL_000c: stfld valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> CS1998Test.Program/'<NotActuallyAsync>d__3'::'<>t__builder'
        IL_0011: ldloc.0
        IL_0012: ldarg.0
        IL_0013: stfld class CS1998Test.Program CS1998Test.Program/'<NotActuallyAsync>d__3'::'<>4__this'
        IL_0018: ldloc.0
        IL_0019: ldc.i4.m1
        IL_001a: stfld int32 CS1998Test.Program/'<NotActuallyAsync>d__3'::'<>1__state'
        IL_001f: ldloc.0
        IL_0020: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> CS1998Test.Program/'<NotActuallyAsync>d__3'::'<>t__builder'
        IL_0025: ldloca.s 0
        IL_0027: call instance void valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<class CS1998Test.Program/'<NotActuallyAsync>d__3'>(!!0&)
        IL_002c: ldloc.0
        IL_002d: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> CS1998Test.Program/'<NotActuallyAsync>d__3'::'<>t__builder'
        IL_0032: call instance class [System.Runtime]System.Threading.Tasks.Task`1<!0> valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task()
        IL_0037: ret
    } // end of method Program::NotActuallyAsync
    
    .class nested private auto ansi sealed beforefieldinit '<NotActuallyAsync>d__3'
        extends [System.Runtime]System.Object
        implements [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine
    {
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
            01 00 00 00
        )
        // Fields
        .field public int32 '<>1__state'
        .field public valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> '<>t__builder'
        .field public class CS1998Test.Program '<>4__this'
    
        // Methods
        .method public hidebysig specialname rtspecialname 
            instance void .ctor () cil managed 
        {
            // Method begins at RVA 0x20fd
            // Code size 8 (0x8)
            .maxstack 8
    
            // {
            IL_0000: ldarg.0
            // (no C# code)
            IL_0001: call instance void [System.Runtime]System.Object::.ctor()
            // }
            IL_0006: nop
            IL_0007: ret
        } // end of method '<NotActuallyAsync>d__3'::.ctor
    
        .method private final hidebysig newslot virtual 
            instance void MoveNext () cil managed 
        {
            .override method instance void [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext()
            // Method begins at RVA 0x2108
            // Code size 58 (0x3a)
            .maxstack 2
            .locals init (
                [0] int32,
                [1] int32,
                [2] class [System.Runtime]System.Exception
            )
    
            // int num = <>1__state;
            IL_0000: ldarg.0
            IL_0001: ldfld int32 CS1998Test.Program/'<NotActuallyAsync>d__3'::'<>1__state'
            IL_0006: stloc.0
            .try
            {
                // result = 42;
                IL_0007: ldc.i4.s 42
                IL_0009: stloc.1
                // }
                IL_000a: leave.s IL_0024
            } // end .try
            catch [System.Runtime]System.Exception
            {
                // catch (Exception exception)
                IL_000c: stloc.2
                // <>1__state = -2;
                IL_000d: ldarg.0
                IL_000e: ldc.i4.s -2
                IL_0010: stfld int32 CS1998Test.Program/'<NotActuallyAsync>d__3'::'<>1__state'
                // <>t__builder.SetException(exception);
                IL_0015: ldarg.0
                IL_0016: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> CS1998Test.Program/'<NotActuallyAsync>d__3'::'<>t__builder'
                IL_001b: ldloc.2
                IL_001c: call instance void valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetException(class [System.Runtime]System.Exception)
                // return;
                IL_0021: nop
                IL_0022: leave.s IL_0039
            } // end handler
    
            // <>1__state = -2;
            IL_0024: ldarg.0
            IL_0025: ldc.i4.s -2
            IL_0027: stfld int32 CS1998Test.Program/'<NotActuallyAsync>d__3'::'<>1__state'
            // <>t__builder.SetResult(result);
            IL_002c: ldarg.0
            IL_002d: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> CS1998Test.Program/'<NotActuallyAsync>d__3'::'<>t__builder'
            IL_0032: ldloc.1
            IL_0033: call instance void valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetResult(!0)
            // }
            IL_0038: nop
    
            IL_0039: ret
        } // end of method '<NotActuallyAsync>d__3'::MoveNext
    
        .method private final hidebysig newslot virtual 
            instance void SetStateMachine (
                class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine
            ) cil managed 
        {
            .custom instance void [System.Diagnostics.Debug]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = (
                01 00 00 00
            )
            .override method instance void [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine)
            // Method begins at RVA 0x2160
            // Code size 1 (0x1)
            .maxstack 8
    
            // }
            IL_0000: ret
        } // end of method '<NotActuallyAsync>d__3'::SetStateMachine
    
    } // end of class <NotActuallyAsync>d__3
    
    .method private hidebysig 
        instance class [System.Runtime]System.Threading.Tasks.Task`1<int32> FullyAsync () cil managed 
    {
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [System.Runtime]System.Type) = (
            01 00 23 43 53 31 39 39 38 54 65 73 74 2e 50 72
            6f 67 72 61 6d 2b 3c 46 75 6c 6c 79 41 73 79 6e
            63 3e 64 5f 5f 34 00 00
        )
        .custom instance void [System.Diagnostics.Debug]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = (
            01 00 00 00
        )
        // Method begins at RVA 0x20b0
        // Code size 56 (0x38)
        .maxstack 2
        .locals init (
            [0] class CS1998Test.Program/'<FullyAsync>d__4'
        )
    
        IL_0000: newobj instance void CS1998Test.Program/'<FullyAsync>d__4'::.ctor()
        IL_0005: stloc.0
        IL_0006: ldloc.0
        IL_0007: call valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create()
        IL_000c: stfld valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> CS1998Test.Program/'<FullyAsync>d__4'::'<>t__builder'
        IL_0011: ldloc.0
        IL_0012: ldarg.0
        IL_0013: stfld class CS1998Test.Program CS1998Test.Program/'<FullyAsync>d__4'::'<>4__this'
        IL_0018: ldloc.0
        IL_0019: ldc.i4.m1
        IL_001a: stfld int32 CS1998Test.Program/'<FullyAsync>d__4'::'<>1__state'
        IL_001f: ldloc.0
        IL_0020: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> CS1998Test.Program/'<FullyAsync>d__4'::'<>t__builder'
        IL_0025: ldloca.s 0
        IL_0027: call instance void valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<class CS1998Test.Program/'<FullyAsync>d__4'>(!!0&)
        IL_002c: ldloc.0
        IL_002d: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> CS1998Test.Program/'<FullyAsync>d__4'::'<>t__builder'
        IL_0032: call instance class [System.Runtime]System.Threading.Tasks.Task`1<!0> valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task()
        IL_0037: ret
    } // end of method Program::FullyAsync
    
    .class nested private auto ansi sealed beforefieldinit '<FullyAsync>d__4'
        extends [System.Runtime]System.Object
        implements [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine
    {
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
            01 00 00 00
        )
        // Fields
        .field public int32 '<>1__state'
        .field public valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> '<>t__builder'
        .field public class CS1998Test.Program '<>4__this'
        .field private int32 '<>s__1'
        .field private valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32> '<>u__1'
    
        // Methods
        .method public hidebysig specialname rtspecialname 
            instance void .ctor () cil managed 
        {
            // Method begins at RVA 0x217b
            // Code size 8 (0x8)
            .maxstack 8
    
            // {
            IL_0000: ldarg.0
            // (no C# code)
            IL_0001: call instance void [System.Runtime]System.Object::.ctor()
            // }
            IL_0006: nop
            IL_0007: ret
        } // end of method '<FullyAsync>d__4'::.ctor
    
        .method private final hidebysig newslot virtual 
            instance void MoveNext () cil managed 
        {
            .override method instance void [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext()
            // Method begins at RVA 0x2184
            // Code size 199 (0xc7)
            .maxstack 3
            .locals init (
                [0] int32,
                [1] int32,
                [2] valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32>,
                [3] class CS1998Test.Program/'<FullyAsync>d__4',
                [4] class [System.Runtime]System.Exception
            )
    
            // int num = <>1__state;
            IL_0000: ldarg.0
            IL_0001: ldfld int32 CS1998Test.Program/'<FullyAsync>d__4'::'<>1__state'
            IL_0006: stloc.0
            .try
            {
                // if (num != 0)
                IL_0007: ldloc.0
                IL_0008: brfalse.s IL_000c
    
                // (no C# code)
                IL_000a: br.s IL_000e
    
                // awaiter = Task.Run(() => 42).GetAwaiter();
                IL_000c: br.s IL_0065
    
                IL_000e: ldsfld class [System.Runtime]System.Func`1<int32> CS1998Test.Program/'<>c'::'<>9__4_0'
                IL_0013: dup
                IL_0014: brtrue.s IL_002d
    
                // (no C# code)
                IL_0016: pop
                // if (!awaiter.IsCompleted)
                IL_0017: ldsfld class CS1998Test.Program/'<>c' CS1998Test.Program/'<>c'::'<>9'
                IL_001c: ldftn instance int32 CS1998Test.Program/'<>c'::'<FullyAsync>b__4_0'()
                IL_0022: newobj instance void class [System.Runtime]System.Func`1<int32>::.ctor(object, native int)
                IL_0027: dup
                IL_0028: stsfld class [System.Runtime]System.Func`1<int32> CS1998Test.Program/'<>c'::'<>9__4_0'
    
                IL_002d: call class [System.Runtime]System.Threading.Tasks.Task`1<!!0> [System.Runtime]System.Threading.Tasks.Task::Run<int32>(class [System.Runtime]System.Func`1<!!0>)
                IL_0032: callvirt instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<!0> class [System.Runtime]System.Threading.Tasks.Task`1<int32>::GetAwaiter()
                IL_0037: stloc.2
                IL_0038: ldloca.s 2
                IL_003a: call instance bool valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32>::get_IsCompleted()
                IL_003f: brtrue.s IL_0081
    
                // num = (<>1__state = 0);
                IL_0041: ldarg.0
                IL_0042: ldc.i4.0
                IL_0043: dup
                IL_0044: stloc.0
                IL_0045: stfld int32 CS1998Test.Program/'<FullyAsync>d__4'::'<>1__state'
                // <>u__1 = awaiter;
                IL_004a: ldarg.0
                IL_004b: ldloc.2
                IL_004c: stfld valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32> CS1998Test.Program/'<FullyAsync>d__4'::'<>u__1'
                // <FullyAsync>d__4 stateMachine = this;
                IL_0051: ldarg.0
                IL_0052: stloc.3
                // <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
                IL_0053: ldarg.0
                IL_0054: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> CS1998Test.Program/'<FullyAsync>d__4'::'<>t__builder'
                IL_0059: ldloca.s 2
                IL_005b: ldloca.s 3
                IL_005d: call instance void valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::AwaitUnsafeOnCompleted<valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32>, class CS1998Test.Program/'<FullyAsync>d__4'>(!!0&, !!1&)
                // return;
                IL_0062: nop
                IL_0063: leave.s IL_00c6
    
                // awaiter = <>u__1;
                IL_0065: ldarg.0
                IL_0066: ldfld valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32> CS1998Test.Program/'<FullyAsync>d__4'::'<>u__1'
                IL_006b: stloc.2
                // <>u__1 = default(TaskAwaiter<int>);
                IL_006c: ldarg.0
                IL_006d: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32> CS1998Test.Program/'<FullyAsync>d__4'::'<>u__1'
                IL_0072: initobj valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32>
                // num = (<>1__state = -1);
                IL_0078: ldarg.0
                IL_0079: ldc.i4.m1
                IL_007a: dup
                IL_007b: stloc.0
                IL_007c: stfld int32 CS1998Test.Program/'<FullyAsync>d__4'::'<>1__state'
    
                // <>s__1 = awaiter.GetResult();
                IL_0081: ldarg.0
                IL_0082: ldloca.s 2
                IL_0084: call instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32>::GetResult()
                IL_0089: stfld int32 CS1998Test.Program/'<FullyAsync>d__4'::'<>s__1'
                // result = <>s__1;
                IL_008e: ldarg.0
                IL_008f: ldfld int32 CS1998Test.Program/'<FullyAsync>d__4'::'<>s__1'
                IL_0094: stloc.1
                // }
                IL_0095: leave.s IL_00b1
            } // end .try
            catch [System.Runtime]System.Exception
            {
                // catch (Exception exception)
                IL_0097: stloc.s 4
                // <>1__state = -2;
                IL_0099: ldarg.0
                IL_009a: ldc.i4.s -2
                IL_009c: stfld int32 CS1998Test.Program/'<FullyAsync>d__4'::'<>1__state'
                // <>t__builder.SetException(exception);
                IL_00a1: ldarg.0
                IL_00a2: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> CS1998Test.Program/'<FullyAsync>d__4'::'<>t__builder'
                IL_00a7: ldloc.s 4
                IL_00a9: call instance void valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetException(class [System.Runtime]System.Exception)
                // return;
                IL_00ae: nop
                IL_00af: leave.s IL_00c6
            } // end handler
    
            // <>1__state = -2;
            IL_00b1: ldarg.0
            IL_00b2: ldc.i4.s -2
            IL_00b4: stfld int32 CS1998Test.Program/'<FullyAsync>d__4'::'<>1__state'
            // <>t__builder.SetResult(result);
            IL_00b9: ldarg.0
            IL_00ba: ldflda valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> CS1998Test.Program/'<FullyAsync>d__4'::'<>t__builder'
            IL_00bf: ldloc.1
            IL_00c0: call instance void valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetResult(!0)
            // }
            IL_00c5: nop
    
            IL_00c6: ret
        } // end of method '<FullyAsync>d__4'::MoveNext
    
        .method private final hidebysig newslot virtual 
            instance void SetStateMachine (
                class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine
            ) cil managed 
        {
            .custom instance void [System.Diagnostics.Debug]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = (
                01 00 00 00
            )
            .override method instance void [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine)
            // Method begins at RVA 0x2268
            // Code size 1 (0x1)
            .maxstack 8
    
            // }
            IL_0000: ret
        } // end of method '<FullyAsync>d__4'::SetStateMachine
    
    } // end of class <FullyAsync>d__4

    In brief, executing a method marked with the async modifier entails the construction and execution of an asynchronous state machine for that method, regardless of whether that method is actually performing any asynchronous work! As I'm sure you can guess, that entails a performance penalty compared to a standard non-async method, which - depending on your use-case - may or may not be significant.

    But this isn't what the CS1998 warning says at all. This warning is intended for the case where you've defined an async method because you need to await something in it, but have simply forgotten to add the await keyword before the asynchronous call.

    Your case is essentially the opposite: you've defined a method as async but you know and intend that it doesn't perform any such work. But the compiler has no way of knowing that - to the compiler it looks exactly the same as the previous case, so you get the same warning.

    To be honest, in that second case you yourself have caused the warning by unnecessarily adding the async keyword to the implementation. You know that the method isn't doing any asynchronous work, so why bother adding the keyword? You're just bloating it for no good reason.

    The warning could certainly be improved to call out the fact that you're basically being silly, and I've opened an issue in the Roslyn repo to hopefully get that done.

    回答4

    It might be too late but it might be useful investigation:

    There is about inner structure of compiled code (IL):

    public static async Task<int> GetTestData()
    {
        return 12;
    }

    it becomes to in 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
    

    And without async and task method:

     public static int GetTestData()
     {
          return 12;
     }

    becomes :

    .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
    

    As you could see the big difference between these methods.

    If you don't use await inside async method and do not care about using of async method (for example API call or event handler) the good idea will convert it to normal sync method (it saves your application performance).

    Updated:

    There is also additional information from microsoft docs:

    async methods need to have an await keyword in their body or they will never yield! This is important to keep in mind. If await is not used in the body of an async method, the C# compiler will generate a warning, but the code will compile and run as if it were a normal method. Note that this would also be incredibly inefficient, as the state machine generated by the C# compiler for the async method would not be accomplishing anything.

  • 相关阅读:
    【Java异常】idea 报错:无效的目标发行版:17 的解决办法
    springboot实现MyBatis分页
    已解决No converter for XXX with preset ContentType ‘application/vnd.msexcel;charset=utf8‘
    mybatis if标签判断boolean等于true或者flase
    Java实现Excel导入和导出
    文件上传报错:Current request is not a multipart request的解决办法
    二维数组内的元素获取交集
    .Net Core MemoryCache 缓存
    SQL Server调用OLE对象
    SQL Server中使用正则表达式
  • 原文地址:https://www.cnblogs.com/chucklu/p/16445736.html
Copyright © 2020-2023  润新知