Task.Delay 사용시기, Thread.Sleep 사용시기


385

Task.DelayThread.Sleep 을 사용할 때 좋은 규칙이 있습니까?

  • 구체적으로, 하나가 다른 것보다 효과적이고 효율적일 수 있도록하는 최소값이 있습니까?
  • 마지막으로 Task.Delay로 인해 비동기 / 대기 상태 시스템에서 컨텍스트 전환이 발생하므로이를 사용하는 오버 헤드가 있습니까?

2
10ms는 컴퓨터 세계에서 많은 사이클입니다 ...
Brad Christie

얼마나 빠를까요? 어떤 성능 문제가 있습니까?
LB

4
더 적절한 질문은 어떤 컨텍스트에서 이들 중 하나를 사용하려고합니까? 해당 정보가 없으면 범위가 너무 넓습니다. 효과 / 효율이란 무엇입니까? 정확성, 전력 효율 등을 언급하고 있습니까? 어떤 상황에서 이것이 중요한지 알고 싶습니다.
James World

4
최소값은 15.625msec이며 클럭 인터럽트 속도보다 작은 값은 적용되지 않습니다. Task.Delay는 항상 시스템을 태 웁니다 .Threading.Timer, Sleep에는 오버 헤드가 없습니다. 아무것도하지 않는 코드를 작성할 때 오버 헤드에 대해 걱정하지 않아도됩니다.
Hans Passant

내가 언급하지는 않았지만 중요하다고 생각하는 것은 Task.Delay가 CancellationToken을 지원한다는 것입니다. 예를 들어, 사이클 프로세스 속도를 늦추기 위해 지연을 사용하면 지연을 중단 할 수 있습니다. 이것은 또한 프로세스를 취소하고 싶을 때 프로세스가 빠르게 응답 할 수 있음을 의미합니다. Thread.Sleep을 사용하면 슬립 사이클 간격을 단축하고 토큰 수동 레이아웃을 확인할 수 있습니다.
Droa

답변:


369

Thread.Sleep현재 스레드를 차단하려는 경우에 사용하십시오 .

Task.Delay현재 스레드를 차단하지 않고 논리적 지연을 원할 때 사용하십시오 .

이러한 방법에서 효율성이 가장 중요한 문제가되어서는 안됩니다. 실제 실제 사용은 I / O 조작을위한 재시도 타이머로서 밀리 초가 아닌 초 단위입니다.


3
재시도 타이머와 같은 기본 사용 사례입니다.
Stephen Cleary

4
또는 메인 루프에서 CPU를 씹고 싶지 않을 때.
Eddie Parker

5
@RoyiNamir : 아니요. "다른 스레드"는 없습니다. 내부적으로 타이머로 구현됩니다.
Stephen Cleary

20
효율성에 대해 걱정하지 않는 제안은 잘못 권고됩니다. Thread.Sleep컨텍스트 전환을 일으키는 현재 스레드를 차단합니다. 스레드 풀을 사용하는 경우 새 스레드가 할당 될 수도 있습니다. 두 가지 작업 모두 상당히 무겁지만 Task.Delayetc 등이 제공하는 협력적인 멀티 태스킹 은 모든 오버 헤드를 피하고 처리량을 최대화하며 취소를 허용하고 더 깨끗한 코드를 제공하도록 설계되었습니다.
Corillian

2
onesi: I would use 동기 메소드 내부에서 대기하는 @LucaCremry Thread.Sleep` 그러나 프로덕션 코드에서는이 작업을 수행하지 않습니다. 내 경험상, Thread.Sleep내가 본 모든 것은 올바르게 수정해야 할 디자인 문제를 나타냅니다.
Stephen Cleary

243

가장 큰 차이점 Task.Delay와는 Thread.Sleep그가되어 Task.Delay비동기 적으로 실행하기위한 것입니다. Task.Delay동기 코드에서 사용하는 것은 의미가 없습니다 . Thread.Sleep비동기 코드에서 사용하는 것은 매우 나쁜 생각 입니다.

일반적으로 당신은 호출 Task.Delay()await키워드 :

await Task.Delay(5000);

또는 지연 전에 코드를 실행하려면 다음을 수행하십시오.

var sw = new Stopwatch();
sw.Start();
Task delay = Task.Delay(5000);
Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
await delay;

이것이 무엇을 인쇄 할 것 같습니까? 0.0070048 초 동안 실행 await delay위를 Console.WriteLine대신 이동하면 5.0020168 초 동안 실행 중으로 인쇄됩니다.

다음과의 차이점을 살펴 보겠습니다 Thread.Sleep.

class Program
{
    static void Main(string[] args)
    {
        Task delay = asyncTask();
        syncCode();
        delay.Wait();
        Console.ReadLine();
    }

    static async Task asyncTask()
    {
        var sw = new Stopwatch();
        sw.Start();
        Console.WriteLine("async: Starting");
        Task delay = Task.Delay(5000);
        Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        await delay;
        Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        Console.WriteLine("async: Done");
    }

    static void syncCode()
    {
        var sw = new Stopwatch();
        sw.Start();
        Console.WriteLine("sync: Starting");
        Thread.Sleep(5000);
        Console.WriteLine("sync: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        Console.WriteLine("sync: Done");
    }
}

이것이 인쇄 할 내용을 예측해보십시오 ...

비동기 : 비동기 시작
: 0.0070048 초 동안 실행
동기화 :
비동기 시작 : 5.0119008 초 동안 실행
비동기 : 완료
동기화 : 5.0020168 초 동안 실행
동기화 : 완료

또한 Thread.Sleep훨씬 더 정확하고 ms 정확도는 실제로 문제가되지 Task.Delay않지만 최소 15-30ms가 걸릴 수 있습니다. 두 함수의 오버 헤드는 ms 정확도와 비교하여 최소입니다 ( Stopwatch더 정확한 것이 필요하면 클래스 사용 ). Thread.Sleep여전히 스레드를 묶고 Task.Delay기다릴 때 다른 작업을 수행하려면 스레드를 해제하십시오.


15
"비동기 코드에서 Thread.Sleep을 사용하는 것은 매우 나쁜 생각"인 이유는 무엇입니까?
sunside

69
@sunside 비동기 코드의 주요 장점 중 하나는 호출 차단을 피함으로써 한 스레드가 여러 작업에서 한 번에 작업 할 수 있도록하는 것입니다. 이를 통해 많은 양의 개별 스레드가 필요하지 않으며 스레드 풀이 한 번에 많은 요청을 처리 할 수 ​​있습니다. 그러나 비동기 코드는 일반적으로 스레드 풀에서 실행되므로 단일 스레드를 불필요하게 차단하면 Thread.Sleep()다른 곳에서 사용할 수있는 전체 스레드가 소비됩니다. Thread.Sleep ()를 사용하여 많은 작업을 실행하면 모든 스레드 풀 스레드가 소모되고 성능이 심각하게 저하 될 수 있습니다.
Ryan

1
가져와 비동기 코드의 개념 async이 사용되도록 권장되는 메소드 의 관점에서 누락되었습니다 . 기본적으로 Thread.Sleep()스레드 풀 스레드에서 실행 하는 것은 좋지 않은 아이디어입니다. 결국 TaskCreationOptions.LongRunning, (추천하지는 않지만) Task.Factory.StartNew()경로를 갈 때가 있습니다.
sunside

6
에 대한 kudosawait wait
Eric Wu

2
@Reyhn이 문서 Tasl.Delay는 시스템 타이머 를 사용합니다. "시스템 클럭은 일정한 속도로"틱 "합니다. 시스템 타이머의 틱 속도는 약 16ms이므로 요청한 모든 지연 시간은 시스템 클럭의 틱 수로 반올림됩니다. 진드기. Task.Delay docs.microsoft.com/en-us/dotnet/api/… 의 msdn 설명서를 참조하여 아래로 스크롤하여 설명을 표시하십시오.
Dorus

28

현재 스레드가 종료되어 사용 Thread.Sleep하고 실행 중이 면을 얻을 수 있습니다 ThreadAbortException. 함께 Task.Delay하면 항상 취소 토큰을 제공하고 우아하게 죽일 수 있습니다. 그게 내가 선택하는 한 가지 이유입니다 Task.Delay. 참조 http://social.technet.microsoft.com/wiki/contents/articles/21177.visual-c-thread-sleep-vs-task-delay.aspx를

또한이 경우 효율성이 가장 중요하지 않다는 데 동의합니다.


2
다음과 같은 상황이 있다고 가정합니다 await Task.Delay(5000). 작업을 죽일 때 얻을 수 TaskCanceledException있지만 스레드는 여전히 살아 있습니다. 산뜻한! :)
AlexMelw

24

무언가를 추가하고 싶습니다. 실제로 Task.Delay타이머 기반 대기 메커니즘입니다. 소스 를 보면 Timer지연을 담당 하는 클래스에 대한 참조를 찾을 수 있습니다. 반면에 Thread.Sleep실제로는 현재 스레드를 잠자기 상태로 만듭니다. 이렇게하면 하나의 스레드 만 차단하고 낭비하게됩니다. 비동기 프로그래밍 모델에서는 Task.Delay()약간의 지연 후에 무언가 (계속)를 원할 경우 항상 사용해야 합니다.


'await Task.Delay ()'는 타이머가 만료 될 때까지 스레드가 다른 작업을 수행 할 수 있도록합니다 (100 % 지우기). 그러나 메소드에 접두사가 'async'가 아니기 때문에 'await'을 사용할 수 없으면 어떻게해야합니까? 그런 다음 'Task.Delay ()'만 호출 할 수 있습니다. 이 경우 스레드가 여전히 차단되어 있지만 Delay ()를 취소 할 수 있다는 이점이 있습니다. 그 맞습니까?
Erik Stroeken

5
@ErikStroeken 취소 토큰을 스레드와 작업 모두에 전달할 수 있습니다. Task.Delay (). Wait ()는 차단하지만 Task.Delay ()는 대기없이 사용하면 작업을 생성합니다. 해당 작업으로 수행하는 작업은 사용자의 몫이지만 스레드는 계속됩니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.