"이 비동기 메서드에는 'await'연산자가 없으며 동 기적으로 실행됩니다."경고에 대해 걱정해야합니까?
일부 비동기 메서드를 노출하는 인터페이스가 있습니다. 보다 구체적으로 Task 또는 Task <T>를 반환하는 메서드가 정의되어 있습니다. async / await 키워드를 사용하고 있습니다.
이 인터페이스를 구현하는 중입니다. 그러나 이러한 방법 중 일부에서는이 구현이 기다릴 것이 없습니다. 따라서 "이 비동기 메서드에는 '대기'연산자가 없으며 동 기적으로 실행됩니다."라는 컴파일러 경고가 표시됩니다.
나는 왜 오류가 발생하는지 이해하지만이 맥락에서 그들에 대해 어떤 조치를 취해야하는지 궁금합니다. 컴파일러 경고를 무시하는 것은 잘못된 것 같습니다.
Task.Run을 기다리면 해결할 수 있다는 것을 알고 있지만 몇 가지 저렴한 작업 만 수행하는 메서드에 대해서는 잘못된 느낌입니다. 또한 실행에 불필요한 오버 헤드를 추가하는 것처럼 들리지만 async 키워드가 존재하기 때문에 이것이 이미 존재하는지 확실하지 않습니다.
경고를 무시해야합니까, 아니면 표시되지 않는 문제를 해결하는 방법이 있습니까?
비동기 단지 방법의 구현 세부 키워드이다; 메서드 서명의 일부가 아닙니다. 특정 메서드 구현 또는 재정의에 기다릴 것이 없으면 async 키워드를 생략하고 Task.FromResult <TResult>를 사용하여 완료된 작업을 반환합니다 .
public Task<string> Foo() // public async Task<string> Foo()
{ // {
Baz(); // Baz();
return Task.FromResult("Hello"); // return "Hello";
} // }
메서드가 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; //
} // }
일부 "비동기"작업은 동기식으로 완료되지만 다형성을 위해 여전히 비동기 호출 모델을 따르는 것이 합리적입니다.
이에 대한 실제 예는 OS I / O API입니다. 일부 장치의 비동기 및 중첩 호출은 항상 인라인으로 완료됩니다 (예 : 공유 메모리를 사용하여 구현 된 파이프에 쓰기). 그러나 그들은 백그라운드에서 계속되는 다중 부분 작업과 동일한 인터페이스를 구현합니다.
Michael Liu는 Task.FromResult를 반환하여 경고를 피할 수있는 방법에 대한 질문에 잘 대답했습니다.
질문의 "경고에 대해 걱정해야합니까?"부분에 대답하겠습니다.
대답은 예입니다!
그 이유 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
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).
'Programing' 카테고리의 다른 글
테스트 중 @Autowired 비공개 필드 삽입 (0) | 2020.11.24 |
---|---|
Angular 지시문 템플릿에 조건부로 데이터 속성 추가 (0) | 2020.11.24 |
경고 : 주소 공간 무작위 화 비활성화 오류 : 작업이 허용되지 않습니다. (0) | 2020.11.24 |
REST 웹 서비스 테스트 (0) | 2020.11.24 |
CoreData 레코드의 재정렬을 구현하는 방법은 무엇입니까? (0) | 2020.11.24 |