답변:
async
/ 를 사용할 때 await
호출 할 때 호출하는 메소드 await FooAsync()
가 실제로 비동기 적으로 실행 된다는 보장은 없습니다 . 내부 구현은 완전히 동기화 된 경로를 사용하여 자유롭게 반환 할 수 있습니다.
차단하지 않고 코드를 비동기식으로 실행해야하는 중요한 API를 만드는 경우 호출 된 메서드가 동 기적으로 (효과적으로 차단) 실행될 가능성이 있으면를 사용 await Task.Yield()
하면 메서드가 비동기식으로 돌아가고 그 시점에서 제어합니다. 나머지 코드는 현재 컨텍스트에서 나중에 실행됩니다 (이 시점에서 여전히 동 기적으로 실행될 수 있음).
"장기 실행"초기화가 필요한 비동기 메소드를 만드는 경우에도 유용합니다.
private async void button_Click(object sender, EventArgs e)
{
await Task.Yield(); // Make us async right away
var data = ExecuteFooOnUIThread(); // This will run on the UI thread at some point later
await UseDataAsync(data);
}
Task.Yield()
호출 하지 않으면 메소드는에 대한 첫 번째 호출까지 동기식으로 실행됩니다 await
.
Task.Run
하여 구현 ExecuteFooOnUIThread
하면 UI 스레드가 아닌 스레드 풀에서 실행됩니다. 을 사용하면 await Task.Yield()
후속 코드가 현재 컨텍스트에서 (나중에) 계속 실행되는 방식으로 비동기식으로 강제합니다. 일반적으로하는 일은 아니지만 이상한 이유로 필요한 경우 옵션이있는 것이 좋습니다.
ExecuteFooOnUIThread()
매우 오래 실행 된 경우 여전히 특정 시점에서 오랫동안 UI 스레드를 차단하고 UI를 응답하지 않게 만드는 것이 맞습니까?
내부적 await Task.Yield()
으로는 현재 동기화 컨텍스트 또는 임의의 풀 스레드 (있는 경우 SynchronizationContext.Current
) 에서 연속을 큐에 넣습니다 null
.
그것은되어 효율적으로 구현 사용자 정의 awaiter한다. 동일한 효과를 생성하는 덜 효율적인 코드는 다음과 같이 간단 할 수 있습니다.
var tcs = new TaskCompletionSource<bool>();
var sc = SynchronizationContext.Current;
if (sc != null)
sc.Post(_ => tcs.SetResult(true), null);
else
ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(true));
await tcs.Task;
Task.Yield()
이상한 실행 흐름 변경에 대한 바로 가기로 사용할 수 있습니다. 예를 들면 다음과 같습니다.
async Task DoDialogAsync()
{
var dialog = new Form();
Func<Task> showAsync = async () =>
{
await Task.Yield();
dialog.ShowDialog();
}
var dialogTask = showAsync();
await Task.Yield();
// now we're on the dialog's nested message loop started by dialog.ShowDialog
MessageBox.Show("The dialog is visible, click OK to close");
dialog.Close();
await dialogTask;
// we're back to the main message loop
}
즉, 적절한 작업 스케줄러 Task.Yield()
로 교체 할 수없는 경우는 생각할 수 없습니다 Task.Factory.StartNew
.
또한보십시오:
var dialogTask = await showAsync();
있습니까?
var dialogTask = await showAsync()
는 await showAsync()
표현식이를 반환하지 않기 때문에 컴파일 하지 않습니다 Task
(없는 경우와 달리 await
). 즉, 그렇게하면 await showAsync()
대화 상자를 닫은 후에 만 실행이 재개됩니다. 그렇습니다. window.ShowDialog
동기 API 이기 때문에 여전히 메시지를 펌핑 하기 때문 입니다. 이 코드에서 대화 상자가 계속 표시되는 동안 계속하고 싶습니다.
Task.Yield()
비동기 재귀를 수행 할 때 스택 오버플로를 방지하는 것이 한 가지 용도입니다 . Task.Yield()
동기 연속을 방지합니다. 그러나 이로 인해 Triynko에서 지적한대로 OutOfMemory 예외가 발생할 수 있습니다. 끝없는 재귀는 여전히 안전하지 않으며 재귀를 루프로 다시 작성하는 것이 좋습니다.
private static void Main()
{
RecursiveMethod().Wait();
}
private static async Task RecursiveMethod()
{
await Task.Delay(1);
//await Task.Yield(); // Uncomment this line to prevent stackoverlfow.
await RecursiveMethod();
}
await Task.Delay(1)
막기에 충분한 것 같습니다 . (콘솔 앱, .NET Core 3.1, C # 8)
Task.Yield()
비동기 메소드의 모의 구현에 사용될 수 있습니다.
await Task.Yield()
힘 비동기 될 수있는 방법, 왜 우리가 "진짜"비동기 코드를 작성 귀찮게 것? 강력한 동기화 방법을 상상해보십시오. 그것은 비동기, 단지 추가하려면async
및await Task.Yield()
시작과 마술, 그것은 비동기 될 것인가? 그것은 모든 동기화 코드를 래핑Task.Run()
하고 가짜 비동기 메서드를 만드는 것과 거의 같습니다 .