UI 스레드 메시지 루프를 앞으로 반복 할 때까지 코드 실행을 연기해야한다면 다음과 같이 할 수 있습니다.
await Task.Factory.StartNew(
() => {
MessageBox.Show("Hello!");
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
이것은await Task.Yield(); MessageBox.Show("Hello!");
, 내가 원하는 경우 작업을 취소 할 수있는 옵션이 있습니다.
기본 동기화 컨텍스트가있는 경우 비슷한 방법으로await Task.Run
풀 스레드에서 계속하려면.
사실, 나는 좋아한다.Task.Factory.StartNew
과Task.Run
이상Task.Yield
두 코드 모두 연속 코드의 범위를 명시 적으로 정의하기 때문입니다.
그래서 어떤 상황에서await Task.Yield()
실제로 유용합니까?
한 가지 상황Task.Yield()
실제로 유용 할 때가있다.await
재귀 적으로 동기식으로 완료된Task
에스. csharp의async
/await
"Zalgo 출시"가능한 경우 연속성을 동 기적으로 실행하여 완전 동기식 재귀 시나리오의 스택이 프로세스가 종료 될만큼 충분히 커질 수 있습니다. 나는 이것이 또한 부분적으로 꼬리 호출 때문에 지원 될 수없는 것 같아요.Task
우회.await Task.Yield()
인라인이 아닌 스케줄러가 계속 실행하도록 예약하므로 스택의 증가를 피하고이 문제를 해결할 수 있습니다.
또한,Task.Yield()
메서드의 동기 부분을 짧게하는 데 사용할 수 있습니다. 호출자가 메소드를 수신해야하는 경우Task
메소드가 어떤 동작을 수행하기 전에,Task.Yield()
강제로 반환Task
그렇지 않으면 자연스럽게 일어날 것입니다. 예를 들어, 다음 로컬 메소드 시나리오에서async
메서드는 자체에 대한 참조를 얻을 수 있습니다.Task
안전하게 (단 하나 동시성에 이것을 달리고 있다고 가정하십시오SynchronizationContext
winforms 또는 via와 같은니트로AsyncContext.Run()
) :
using Nito.AsyncEx;
using System;
using System.Threading.Tasks;
class Program
{
// Use a single-threaded SynchronizationContext similar to winforms/WPF
static void Main(string[] args) => AsyncContext.Run(() => RunAsync());
static async Task RunAsync()
{
Task<Task> task = null;
task = getOwnTaskAsync();
var foundTask = await task;
Console.WriteLine($"{task?.Id} == {foundTask?.Id}: {task == foundTask}");
async Task<Task> getOwnTaskAsync()
{
// Cause this method to return and let the 「task」 local be assigned.
await Task.Yield();
return task;
}
}
}
산출:
3 == 3: True
나는 실제 상황을 생각할 수 없다는 점에 유감스럽게 생각한다.async
방법은 뭔가를하는 가장 좋은 방법입니다. 내가 보여준 것처럼 당신이 트릭을 할 수 있다는 것을 알 때가끔 유용 할 수 있지만, 너무 위험한 경향이 있습니다. 종종 더 잘 읽고 더 읽기 쉬운 방법으로 데이터를 전달할 수 있습니다. 예를 들어 로컬 메서드에 자체 참조를 전달할 수 있습니다.Task
~을 사용하여TaskCompletionSource
대신 :
using System;
using System.Threading.Tasks;
class Program
{
// Fully free-threaded! Works in more environments!
static void Main(string[] args) => RunAsync().Wait();
static async Task RunAsync()
{
var ownTaskSource = new TaskCompletionSource<Task>();
var task = getOwnTaskAsync(ownTaskSource.Task);
ownTaskSource.SetResult(task);
var foundTask = await task;
Console.WriteLine($"{task?.Id} == {foundTask?.Id}: {task == foundTask}");
async Task<Task> getOwnTaskAsync(
Task<Task> ownTaskTask)
{
// This might be clearer.
return await ownTaskTask;
}
}
}
산출:
2 == 2: True
SynchronizationContext
그래도 그것과 관련이있다. 이런 식으로하면 그래픽 응용 프로그램에서 GUI 스레드가 아닌 것 같습니다. 그러나 Task API를 사용하여 반복적으로 작업하는 경우 기다려야 할 작업이 완료되지 않았다고 확신 할 수 없으므로 수행해야 할 수도 있습니다. 당신은 언제나 그것을 태스크에 조건부로 만들 수 있습니다.Task.IsCompleted
및 카운터. - binki
비동기 태스크가 값을 리턴하도록하려는 경우를 고려하십시오.
기존 동기 방식 :
public int DoSomething()
{
return SomeMethodThatReturnsAnInt();
}
비동기를 만들려면 비동기 키워드를 추가하고 반환 유형을 변경하십시오.
public async Task<int> DoSomething()
Task.Factory.StartNew ()를 사용하려면 메서드의 한 줄 본문을 다음과 같이 변경하십시오.
// start new task
var task = Task<int>.Factory.StartNew(
() => {
return SomeMethodThatReturnsAnInt();
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext() );
// await task, return control to calling method
await task;
// return task result
return task.Result;
사용하는 경우 한 줄 추가 vs.await Task.Yield()
// this returns control to the calling method
await Task.Yield();
// otherwise synchronous method scheduled for async execution by the
// TaskScheduler of the calling thread
return SomeMethodThatReturnsAnInt();
후자는 훨씬 간결하고 읽기 쉽고 실제로 기존 방법을 많이 변경하지 않습니다.
return await Task.Factory.StartNew(() => SomeMethodThatReturnsAnInt(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
. 그럼에도 불구하고 나는 +1 점을 본다. 이 문제는 제어 흐름을 다소 비 직관적으로 변경하는 것으로 논의됩니다.이리. - noseratioawait task;
선 - MohoSomeMethodThatReturnsAnInt
으로async
간단히 할 수 있습니다.public Task<int> DoSomething() { return Task.FromResult(SomeMethodThatReturnsAnInt()); }
. 또는,async
예외 전파의 의미:public async Task<int> DoSomething() { return await Task.FromResult(SomeMethodThatReturnsAnInt()); }
. 분명히,await Task.Yield()
여기에 중복되고 바람직하지 않습니다. 내 표를 취소하는 것에 대해 죄송합니다. - noseratio
Task.Yield()
그렇지 않으면 동기식 부분에서 "펀칭 구멍"에 적합합니다.async
방법.
개인적으로 내가 스스로 취소 한 경우에 유용하다는 것을 알았습니다.async
방법 (자체적으로CancellationTokenSource
매우 짧은 기간 (즉, 상호 의존적 인 UI 요소의 이벤트 핸들러)에 여러 번 호출 할 수있는 이전에 생성 된 인스턴스를 취소합니다. 이러한 상황에서Task.Yield()
다음에IsCancellationRequested
빨리 확인하십시오.CancellationTokenSource
어쨌든 결과가 폐기 될 잠재적으로 값 비싼 작업을 수행하는 것을 막을 수 있습니다.
다음은 SelfCancellingAsync에 마지막으로 대기중인 호출 만 비싼 작업을 수행하고 완료까지 실행되는 예제입니다.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskYieldExample
{
class Program
{
private static CancellationTokenSource CancellationTokenSource;
static void Main(string[] args)
{
SelfCancellingAsync();
SelfCancellingAsync();
SelfCancellingAsync();
Console.ReadLine();
}
private static async void SelfCancellingAsync()
{
Console.WriteLine("SelfCancellingAsync starting.");
var cts = new CancellationTokenSource();
var oldCts = Interlocked.Exchange(ref CancellationTokenSource, cts);
if (oldCts != null)
{
oldCts.Cancel();
}
// Allow quick cancellation.
await Task.Yield();
if (cts.IsCancellationRequested)
{
return;
}
// Do the "meaty" work.
Console.WriteLine("Performing intensive work.");
var answer = await Task
.Delay(TimeSpan.FromSeconds(1))
.ContinueWith(_ => 42, TaskContinuationOptions.ExecuteSynchronously);
if (cts.IsCancellationRequested)
{
return;
}
// Do something with the result.
Console.WriteLine("SelfCancellingAsync completed. Answer: {0}.", answer);
}
}
}
여기서 목표는 동일한 코드에서 동 기적으로 실행되는 코드를 허용하는 것입니다.SynchronizationContext
비동기 메소드에 대한 대망 호출이 반환 된 직후 (첫 번째 호출시await
)를 사용하여 async 메서드의 실행에 영향을주는 상태를 변경합니다. 이것은 많은 사람들이 성취 한 것과 비슷합니다.Task.Delay
(나는 여기서 0이 아닌 지연 기간을 말하고있다.) 그러나실제의, 잠재적으로 눈에 띄는 지연, 어떤 경우에는 반갑지 않을 수 있습니다.
Task.Yield
또는 도우미Task.Delay
,여기에 내 버전이있다.취소 및 다시 시작 패턴. - noseratioasync void
서명SelfCancellingAsync
. - noseratiovoid
왜냐하면Task
기다려야한다고 제안합니다. 여기서는 그렇지 않습니다. 또한 예외를 throw하는 것과는 대조적으로 취소가 감지 될 때 메소드에서 복귀하는 이유 (일반적으로 호출자와의 통신 취소에 대해 선호하는 방식 임)입니다. - Kirill Shlenskiyasync Task
예외를 던지고 호출자가 처리하도록하는 것이 바람직한 방법입니다. 의 경우async void
그러나 예외 처리는 일반적으로 async 메서드 자체의 본문에 연결해야합니다. - Kirill ShlenskiySelfCancellingAsync
. 그러나 결국 외부에서 취소를 요청할 것입니다 (예 :Main
메소드가 종료되고 앱이 종료됩니다). 이 시점에서 대기중인 작업이 시작될 때까지 기다리는 것이 좋습니다.SelfCancellingAsync
그것이 정상적으로 끝내도록. 귀하의 접근 방식으로 이것이 어떻게 수행되는지 볼 수는 없습니다. - noseratio
Task.Yield
에 대한 대안이 아니다.Task.Factory.StartNew
또는Task.Run
. 그들은 완전히 다릅니다. 때를await
Task.Yield
스레드를 차단하지 않고 현재 스레드의 다른 코드를 실행할 수 있습니다. 그것을 기다리는 것 같이 생각하십시오.Task.Delay
, 제외하고Task.Yield
특정 시간 동안 기다리지 않고 작업이 완료 될 때까지 기다립니다.
참고 : 사용하지 마십시오.Task.Yield
UI 스레드에서 UI가 항상 반응 형으로 유지된다고 가정합니다. 항상 그런 것은 아닙니다.
Task.Yield
및 기타 사용자 정의 대기자가 작동합니다. 이 점에서,나는 대부분 당신의 마지막 문장 외에 모든 것에 동의하지 않습니다. - noseratioTask.Yield
특정 시간 동안 기다리는 것이 아니라 작업이 완료 될 때까지 기다립니다. "- 다른 작업이 완료 될 때까지 기다리지 않습니다. "준비가 완료되었습니다"연속을 예약하고 라인에서 대기중인 다음 "기존 준비"연속으로 전환합니다 (자체 일 수도 있음). 즉, 이것은 당신의 작업이 그 시점에서 동기가되는 것을 멈추게합니다. - binki
Task.Yield
단위 테스트 및모호한 ASP.NET 문제를 해결하려면async
방법~해서는 안된다.동 기적으로 완료하다. - Stephen ClearyMessageBox.Show()
지나치지 않고IWin32Window owner
논의창에 포커스가 없을 때 해당 코드가 실행되면 메시지 상자가 "아래에 표시됩니다"라는 메시지 상자가 나타날 수 있습니다. 이것은 GUI 스레드에서 수행하는 경우 특히 혼란 스럽습니다. 또한, 통과하면IWin32Window
에MessageBox.Show()
UI 스레드에서 그렇게해야합니다. 그래서, 그 경우, 당신은~해서는 안된다.용도Task.Run()
과절대로 필요한 것패스TaskScheduler
에StartNew()
. - binkiMessageBox.Show()
왜냐하면MessageBox.Show()
메시지 대기열을 펌핑하고 GUI에 예정된 연속성을 실행합니다.SynchronizationContext
. 즉, 양식을 계속 업데이트하고 사물을 표시 할 수 있습니다.async
당신이 평소처럼MessageBox.Show()
보여주고있다. - binki