나에 대해 읽었 어.Task.Yield
, 그리고 자바 스크립트 개발자로서 저는 그것이 그것이 직업이라고 말할 수 있습니다.정확하게같은setTimeout(function (){...},0);
메인 쓰레드가 다른 것들을 처리하도록하는 관점에서 :
"모든 힘을 다 쓰지 말고 시간에서 해방하십시오 - 그래서 다른 사람들은 너무 ... "
js에서는 긴 루프에서 특히 작동합니다. (브라우저를 정지시키지 마라.)
그러나 나는이 예를 보았다.이리:
public static async Task < int > FindSeriesSum(int i1)
{
int sum = 0;
for (int i = 0; i < i1; i++)
{
sum += i;
if (i % 1000 == 0) ( after a bulk , release power to main thread)
await Task.Yield();
}
return sum;
}
JS 프로그래머로서 나는 그들이 무엇을했는지 이해할 수있다.
그러나 C #프로그래머로서 나는 스스로에게 묻습니다. 왜 그것을위한 작업을하지 않습니까?
public static async Task < int > FindSeriesSum(int i1)
{
//do something....
return await MyLongCalculationTask();
//do something
}
의문
Js에서는 Task를 열 수 없습니다 (네, 저는 실제로 웹 작업자와 함께 할 수 있습니다.). 하지만 C #제가 할수 있어요.
그렇다면- 내가 풀어줄 수있는 동안 때때로 풀어주는 것을 왜 귀찮게 하는가?
참조 추가 :
에서이리:
에서이리(다른 eBook) :
볼 때 :
await Task.Yield();
당신은 이런 식으로 생각할 수 있습니다 :
await Task.Factory.StartNew(
() => {},
CancellationToken.None,
TaskCreationOptions.None,
SynchronizationContext.Current != null?
TaskScheduler.FromCurrentSynchronizationContext():
TaskScheduler.Current);
이 모든 작업은 향후 비동기 적으로 연속성이 유지되도록합니다. 으로비동기 적으로실행 제어가 호출자에게 돌아갈 것입니다.async
메소드와 연속 콜백은아니동일한 스택 프레임에서 발생합니다.
정확히 어떤 스레드에서 발생하는지는 호출자 스레드의 동기화 컨텍스트에 따라 다릅니다.
UI 스레드의 경우, 메시지 루프의 미래의 반복에 따라 계속 될 것입니다.Application.Run
(WinForms) 또는Dispatcher.Run
(WPF). 내부적으로는 Win32PostMessage
API는 사용자 정의 메시지를 UI 스레드의 메시지 대기열에 게시합니다. 그만큼await
이 메시지가 펌핑되고 처리되면 계속 콜백이 호출됩니다. 정확히 언제 이런 일이 발생할지 완전히 통제 할 수 없습니다.
게다가 Windows에는 메시지를 펌핑하기위한 우선 순위가 있습니다.정보 : 창 메시지 우선 순위. 가장 관련성이 높은 부분 :
이 계획에서 우선 순위는 3 단계로 간주 될 수 있습니다. 모든 게시 된 메시지는 사용자 입력 메시지보다 우선 순위가 높기 때문에 그들은 다른 대기열에 있습니다. 그리고 모든 사용자 입력 메시지는 WM_PAINT 및 WM_TIMER 메시지보다 높은 우선 순위.
그래서, 만약 당신이await Task.Yield()
UI 응답 성을 유지하려고 시도 할 때 메시지 루프를 반환하기 위해 UI 스레드의 메시지 루프를 실제로 막을 위험이 있습니다. 일부 보류중인 사용자 입력 메시지는 물론WM_PAINT
과WM_TIMER
게시 된 계속 메시지보다 우선 순위가 낮습니다. 따라서, 만약 당신이await Task.Yield()
단단한 루프에서 여전히 UI를 차단할 수 있습니다.
이것은 자바 스크립트와 다른 점입니다.setTimer
당신이 질문에서 언급 한 비유. 에이setTimer
콜백이 호출됩니다.후모든 사용자 입력 메시지가 브라우저의 메시지 펌프에 의해 처리되었습니다.
그래서,await Task.Yield()
UI 스레드에서 백그라운드 작업을 수행하는 데 적합하지 않습니다. 실제로 UI 스레드에서 백그라운드 프로세스를 실행할 필요는 거의 없지만 가끔씩 수행합니다. 편집기 구문 강조, 맞춤법 검사 등.이 경우 프레임 워크의 유휴 인프라를 사용하십시오.
예 : WPF로 할 수있는 작업await Dispatcher.Yield(DispatcherPriority.ApplicationIdle)
:
async Task DoUIThreadWorkAsync(CancellationToken token)
{
var i = 0;
while (true)
{
token.ThrowIfCancellationRequested();
await Dispatcher.Yield(DispatcherPriority.ApplicationIdle);
// do the UI-related work item
this.TextBlock.Text = "iteration " + i++;
}
}
WinForms의 경우,Application.Idle
행사:
// await IdleYield();
public static Task IdleYield()
{
var idleTcs = new TaskCompletionSource<bool>();
// subscribe to Application.Idle
EventHandler handler = null;
handler = (s, e) =>
{
Application.Idle -= handler;
idleTcs.SetResult(true);
};
Application.Idle += handler;
return idleTcs.Task;
}
그것을 추천합니다UI 스레드에서 실행되는 백그라운드 작업이 반복 될 때마다 50ms를 초과하지 않아야합니다.
UI가 아닌 스레드의 경우동기화 문맥이없는 경우,await Task.Yield()
연속을 임의의 풀 스레드로 전환합니다. 그것이 될 것이라는 보장은 없다.다른스레드를 현재 스레드에서 가져온 경우에만비동기계속. 만약ThreadPool
그것은 굶주리고있다, 그것은 동일한 실에 계속을 계획 할지도 모른다.
ASP.NET에서,하고있다.await Task.Yield()
doesn''t는 전혀 언급하지 않는다.@ StephenCleary의 대답. 그렇지 않으면 여분의 스레드 스위치로 웹 응용 프로그램 성능을 해칠뿐입니다.
그래서,await Task.Yield()
유능한?IMO,별로. 그것은을 통해 연속을 실행하는 지름길로 사용될 수 있습니다.SynchronizationContext.Post
또는ThreadPool.QueueUserWorkItem
, 만약 당신이 정말로 당신의 메소드의 일부에 비동기를 부과해야한다면.
인용 한 책에 관해서, 제 의견으로는Task.Yield
틀렸다. 나는 위의 UI 스레드가 왜 틀린 지 설명했다. UI가 아닌 풀 스레드의 경우"실행할 스레드의 다른 작업", 같은 사용자 지정 작업 펌프를 실행하지 않는 한스티븐 투브AsyncPump
.
의견에 답변하도록 업데이트 됨 :
... 어떻게 asynchronouse 작업 및 동일한 스레드에 머물 수 있습니다. ? ..
간단한 예를 들면 다음과 같습니다. WinForms app :
async void Form_Load(object s, object e)
{
await Task.Yield();
MessageBox.Show("Async message!");
}
Form_Load
호출자에게 반환됩니다 (해고 된 WinFroms 프레임 워크 코드).Load
이벤트)를 실행 한 다음 메시지 상자가 비동기 적으로 표시됩니다.Application.Run()
. 연속 콜백은 다음과 함께 대기합니다.WinFormsSynchronizationContext.Post
내부적으로 UI 스레드의 메시지 루프에 비공개 Windows 메시지를 게시합니다. 콜백은이 메시지가 펌핑 될 때 실행되며 여전히 동일한 스레드에서 수행됩니다.
콘솔 앱에서는 다음과 같은 유사한 직렬화 루프를 실행할 수 있습니다.AsyncPump
위에 언급했듯이.
SynchronizationContext
. 봤어?AsyncPump
? 결국 비동기는 멀티 스레딩을 가정하지 않습니다. 장래에 결과를 제공 할 것입니다. 참고로,ThreadPool
s.context가 없으면 동일한 스레드에서 계속이 발생할 가능성이 있습니다 (예 : 동일한 풀 스레드가 비동기 I / O 작업의 완료를 나중에 시작할 수 있음). - noseratioprotected virtual Task OverridableMethod() { return Task.Task.CompletedTask; }
. 노트async
가상 메서드 서명의 일부가 아니므로 파생 클래스에서이 메서드를 재정의 할 때 여전히 사용할 수 있습니다. - noseratio
나는 단지 발견했다.Task.Yield
두 가지 시나리오에서 유용합니다.
아니요. 정확히 사용법은 아닙니다.setTimeout
컨트롤을 UI로 반환합니다. Javascript에서는 항상 UI 업데이트를setTimeout
보류중인 UI 작업은 타이머보다 우선 순위가 높습니다. 그러나 최소 지연 시간은 몇 밀리 초입니다.await Task.Yield();
하지 않습니다.
yield가 주 스레드에서 수행되도록 보장 할 수는 없지만 반대로 yield를 호출하는 코드는 UI 작업보다 우선 순위가 높습니다.
"대부분의 UI에서 UI 스레드에있는 동기화 컨텍스트 환경은 종종 컨텍스트에 게시 된 작업의 우선 순위를 높입니다. 입력 및 렌더링 작업보다. 이런 이유로 기다릴 필요가 없습니다. Task.Yield (); UI를 반응 적으로 유지합니다. "
AsParallel
방법. - Guffa
무엇보다도 먼저 내가 분명히하자.Yield
정확히 같은 것은 아니다.setTimeout(function (){...},0);
. JS는 단일 스레드 환경에서 실행되므로 다른 작업을 수행 할 수있는 유일한 방법입니다. 거의협동 멀티 태스킹. .net은 명시 적 멀티 스레딩 기능을 갖춘 선점 형 멀티 태스킹 환경에서 실행됩니다.
지금으로 돌아 가기Thread.Yield
. 내가 말했듯이 .net은 선점 시대에 살지만, 그보다 조금 더 복잡합니다. 기음#await/async
상태 머신에 의해 통치되는 멀티 태스킹 모드의 흥미로운 혼합을 만듭니다. 그래서 생략하면Yield
귀하의 코드에서 그것은 단지 스레드를 차단하고 그게 전부입니다. 만약 당신이 그것을 정기적 인 작업으로 만들고 start (또는 thread)를 호출하면 task.Result가 호출 될 때 스레드를 병렬로 호출하고 나중에 호출하는 것을 막을 것입니다. 네가 할 때 어떻게 될까?await Task.Yield();
더 복잡합니다. 논리적으로 JS와 비슷한 호출 코드를 차단 해제하고 실행을 계속합니다. 실제로 무엇을하는지 - 다른 스레드를 선택하여 스레드를 호출하는 선점 환경에서 실행을 계속합니다. 그래서 그것은 처음까지 스레드를 호출합니다.Task.Yield
그리고 나서 그것은 자신의 것입니다. 후속 통화 :Task.Yield
명백하게 아무것도하지 마라.
간단한 데모 :
class MainClass
{
//Just to reduce amont of log itmes
static HashSet<Tuple<string, int>> cache = new HashSet<Tuple<string, int>>();
public static void LogThread(string msg, bool clear=false) {
if (clear)
cache.Clear ();
var val = Tuple.Create(msg, Thread.CurrentThread.ManagedThreadId);
if (cache.Add (val))
Console.WriteLine ("{0}\t:{1}", val.Item1, val.Item2);
}
public static async Task<int> FindSeriesSum(int i1)
{
LogThread ("Task enter");
int sum = 0;
for (int i = 0; i < i1; i++)
{
sum += i;
if (i % 1000 == 0) {
LogThread ("Before yield");
await Task.Yield ();
LogThread ("After yield");
}
}
LogThread ("Task done");
return sum;
}
public static void Main (string[] args)
{
LogThread ("Before task");
var task = FindSeriesSum(1000000);
LogThread ("While task", true);
Console.WriteLine ("Sum = {0}", task.Result);
LogThread ("After task");
}
}
결과는 다음과 같습니다.
Before task :1
Task enter :1
Before yield :1
After yield :5
Before yield :5
While task :1
Before yield :5
After yield :5
Task done :5
Sum = 1783293664
After task :1
이사하면Task.Yield
메서드의 맨 위에 비동기로 시작하여 호출 스레드를 차단하지 않습니다.
결론:Task.Yield
동기화 및 비동기 코드를 혼합 할 수 있습니다. 다소 더 현실적인 시나리오 : 계산 작업이 많고 로컬 캐시 및 태스크가 필요합니다.CalcThing
. 이 메서드에서는 항목이 캐시에 있는지 확인합니다 (예인 경우). 항목이없는 경우 항목을 반환합니다.Yield
그것을 계산하기 위해 진행한다. 실제로 책의 샘플은 무의미합니다. 아무런 도움이되지 않기 때문입니다. GUI 인터랙티비티에 대한 그들의 언급은 잘못되었다. (UI 쓰레드는Yield
, 당신은 결코 그것을해서는 안됩니다.MSDN"Task.Yield ()를 기다리는 것에 의존하지 말고 UI를 반응 적으로 유지하십시오."
setTimeout(...,0 )
- Royi Namir
장기 실행 함수가 백그라운드 스레드에서 실행될 수 있다고 가정합니다. 예를 들어 UI 상호 작용이 있기 때문에 그렇지 않은 경우 UI가 실행되는 동안 UI 차단을 방지 할 방법이 없기 때문에 실행 시간이 사용자에게 문제가되지 않도록 충분히 짧게 유지해야합니다.
또 다른 가능성은 백그라운드 스레드보다 장기 실행 기능이 더 많다는 것입니다. 이 시나리오에서는 몇 가지 함수가 스레드를 모두 차지하지 못하도록하는 것이 더 좋을 수도 있습니다 (또는 중요하지 않을 수도 있습니다.).
await
. 권리 ? - Royi NamirLongRunning
그것보다는 오히려yield
여기 저기. - Servy
나는 Task.Yield를 사용할 때 진짜 응답을 아무도 제공하지 않았다고 생각한다. 태스크가 결코 끝나지 않는 루프 (또는 긴 동기화 작업)를 사용하고 잠재적으로 스레드 풀 스레드를 독점적으로 보유 할 수 있으면 (다른 스레드가이 스레드를 사용할 수 없게되는 경우) 대부분 필요합니다. 루프 내부에서 코드가 동 기적으로 실행되는 경우 이런 일이 발생할 수 있습니다. 그만큼Task.Yield reschedules스레드 풀 대기열에 대한 작업과 스레드를 대기 한 다른 작업을 실행할 수 있습니다.
예제 :
CancellationTokenSource cts;
void Start()
{
cts = new CancellationTokenSource();
// run async operation
var task = Task.Run(() => SomeWork(cts.Token), cts.Token);
// wait for completion
// after the completion handle the result/ cancellation/ errors
}
async Task<int> SomeWork(CancellationToken cancellationToken)
{
int result = 0;
bool loopAgain = true;
while (loopAgain)
{
// do something ... means a substantial work or a micro batch here - not processing a single byte
loopAgain = /* check for loop end && */ cancellationToken.IsCancellationRequested;
if (loopAgain) {
// reschedule the task to the threadpool and free this thread for other waiting tasks
await Task.Yield();
}
}
cancellationToken.ThrowIfCancellationRequested();
return result;
}
void Cancel()
{
// request cancelation
cts.Cancel();
}
Task.Yield()
동료들에게 실행할 수있는 기회를 제공합니다. 이와 같은 동시 CPU 바인딩 시나리오를위한 더 나은 옵션이 있습니다.Parallel
또는TPL 데이터 흐름. - noseratio
Task.Yield
UI를 응답으로 유지하십시오. 또한,async
과await
UI 스레드를 비우지 마십시오. 계산 시간이 오래 걸리면 다음을 사용해야합니다.Task.Run
. - Stephen Clearyawait Task.Yield()
매 초마다 다른 짧은 실행 작업은 오랫동안 대기열에 대기하지 않고 기회를 얻습니다. 이것이 공정한 행동이라고 생각하지 않으십니까? - springy76await Task.Yield()
. - Stephen Cleary