[C#] Thread.Sleep이 그렇게 해로운 이유

나는 종종 Thread.Sleep();사용해서는 안된다고 언급하는 것을 보았지만 이것이 왜 그렇게되었는지 이해할 수 없습니다. Thread.Sleep();문제를 일으킬 수 있다면 동일한 결과를 가져도 안전 할 수있는 대체 솔루션이 있습니까?

예.

while(true)
{
    doSomework();
    i++;
    Thread.Sleep(5000);
}

다른 하나는 :

while (true)
{
    string[] images = Directory.GetFiles(@"C:\Dir", "*.png");

    foreach (string image in images)
    {
        this.Invoke(() => this.Enabled = true);
        pictureBox1.Image = new Bitmap(image);
        Thread.Sleep(1000);
    }
}



답변

호출에 대한 문제 Thread.Sleep아르는 간결하게 여기 아주 설명 :

Thread.SleepMTA 스레드에서 테스트 / 디버깅하는 동안 긴 작업을 시뮬레이션합니다. .NET에서는 다른 이유가 없습니다.

Thread.Sleep(n)n
밀리 초 내에 발생할 수있는 최소한 타임 슬라이스 (또는 스레드 퀀텀) 수만큼 현재 스레드를 차단하는 것을 의미합니다 . 타임 슬라이스의 길이는 Windows 버전 / 유형과 프로세서에 따라 다르며 일반적으로 15 ~ 30 밀리 초입니다. 이것은 스레드가 거의 n밀리 초 이상 차단된다는 것을 의미합니다 . 스레드가 정확히 n밀리 초 후에 다시 깨어날 가능성 은 거의 불가능한 일입니다. 따라서 Thread.Sleep타이밍에는 무의미합니다 .

스레드는 제한된 리소스이며 생성하는 데 약 200,000 회, 파괴하는 데 약 100,000 회가 걸립니다. 기본적으로 스택을 위해 1MB의 가상 메모리를 예약하고 각 컨텍스트 스위치에 대해 2,000-8,000 사이클을 사용합니다. 이것은 대기중인 스레드를
엄청난 낭비로 만듭니다.

선호하는 솔루션 : WaitHandles

가장 많이 만든 실수는 Thread.Sleepwhile-construct ( demo and answer , nice blog-entry ) 와 함께 사용하는 것 입니다.

편집 :
내 대답을 향상시키고 싶습니다.

두 가지 사용 사례가 있습니다.

  1. 계속해야 할 특정 기간을 알고 있기 때문에 기다리고 있습니다 Thread.Sleep( System.Threading.Timer또는 유사 항목 사용).

  2. 일부 조건이 때때로 변경되기 때문에 기다리고 있습니다. 키워드는 시간이 있습니다 ! 조건 검사가 코드 도메인에 있으면 WaitHandles를 사용해야합니다. 그렇지 않으면 외부 구성 요소가 일종의 후크를 제공해야합니다. 그렇지 않으면 디자인이 나쁘지 않습니다!

내 대답은 주로 사용 사례 2를 다룹니다.


답변

시나리오 1-비동기 작업 완료 대기 : 스레드가 다른 스레드의 작업이 완료되기를 기다리는 시나리오에서 WaitHandle / Auto | ManualResetEvent를 사용해야한다는 데 동의합니다.

시나리오 2-while 루프 타이밍 : 그러나 조잡한 타이밍 메커니즘 (while + Thread.Sleep)은 차단 된 스레드가 “깨어나야하는 *” 정확한 시기를 알 필요가없는 99 %의 응용 프로그램에 완벽하게 적합합니다 . 스레드를 생성하기위한 200k 주기도 유효하지 않습니다. 어쨌든 타이밍 루프 스레드를 생성해야하고 200k주기는 또 다른 큰 숫자입니다 (파일 / 소켓 / db 호출을 여는주기가 몇 번입니까?).

while + Thread.Sleep이 작동한다면 왜 복잡할까요? 구문 변호사 만이 실용적 일 것입니다 !


답변

누구에게나 도움이 될 수도 있고 아닐 수도있는 코딩 정치적인 관점에서이 질문에 답하고 싶습니다. 그러나 특히 9-5 명의 기업 프로그래머를 대상으로하는 도구를 다룰 때 문서를 작성하는 사람들은 “안된다”및 “절대”와 같은 단어를 사용하는 경향이 있습니다. ‘하고 왜’.

C # 세계에서 제가 가장 좋아하는 몇 가지는 “lock (this) 호출 안 함”또는 “GC.Collect () 호출 안 함”이라고 말합니다. 이 두 가지는 많은 블로그와 공식 문서에서 강력하게 선언되었으며 IMO는 완전히 잘못된 정보입니다. 어떤 수준에서는 이러한 잘못된 정보가 그 목적에 부합합니다. 초보자가 대안을 완전히 연구하기 전에 이해하지 못하는 일을하지 않도록하면서 동시에 검색 엔진을 통해 실제 정보를 찾기 어렵게 만듭니다. “왜 안 되는가?”라는 질문에 대한 답을 제공하지 않는 동안 아무것도하지 말라고 말하는 기사를 가리키는 것 같습니다.

정치적으로, 그것은 사람들이 “좋은 디자인”또는 “나쁜 디자인”이라고 생각하는 것으로 귀결됩니다. 공식 문서가 내 응용 프로그램의 디자인을 지시해서는 안됩니다. sleep ()을 호출하지 않아야하는 진정한 기술적 이유가 있다면 IMO 문서는 특정 시나리오에서 호출해도 괜찮다고 명시해야하지만 시나리오 독립적이거나 다른 것에 더 적합한 대체 솔루션을 제공 할 수도 있습니다. 시나리오.

“sleep ()”을 명확하게 호출하는 것은 데드 라인이 실제 시간 용어로 명확하게 정의 된 많은 상황에서 유용하지만 sleep ()을 던지기 전에 고려하고 이해해야하는 대기 및 신호 스레드를위한 더 정교한 시스템이 있습니다. )를 코드에 추가하고 코드에 불필요한 sleep () 문을 던지는 것은 일반적으로 초보자의 전술로 간주됩니다.


답변

사람들이주의하는 예제의 1) .spinning 및 2) .polling 루프 는 Thread.Sleep () 부분이 아닙니다. Thread.Sleep ()은 일반적으로 회전하거나 폴링 루프에있는 코드를 쉽게 개선하기 위해 추가되므로 “나쁜”코드와 관련이 있습니다.

또한 사람들은 다음과 같은 작업을 수행합니다.

while(inWait)Thread.Sleep(5000); 

여기서 변수 inWait가 스레드로부터 안전한 방식으로 액세스되지 않아 문제가 발생합니다.

프로그래머가보고 싶은 것은 이벤트 및 시그널링 및 잠금 구조에 의해 제어되는 스레드이며, 그렇게하면 Thread.Sleep ()이 필요하지 않으며 스레드로부터 안전한 변수 액세스에 대한 우려도 제거됩니다. 예를 들어, FileSystemWatcher 클래스와 관련된 이벤트 처리기를 만들고 이벤트를 사용하여 루프 대신 두 번째 예제를 트리거 할 수 있습니까?

Andreas N.이 언급했듯이 Joe Albahari의 Threading in C #을 읽으면 정말 좋습니다.


답변

Sleep은 사용자가 제어 할 수없는 독립 프로그램이 종종 일반적으로 사용되는 리소스 (예 : 파일)를 사용할 수 있고, 프로그램이 실행될 때 액세스해야하는 경우, 리소스가 이러한 리소스에서 사용 중일 때 사용됩니다. 다른 프로그램에서 사용이 차단됩니다. 이 경우 코드의 리소스에 액세스하는 경우 리소스 액세스를 try-catch (리소스에 액세스 할 수없는 경우 예외를 포착하기 위해)에 넣고이를 while 루프에 넣습니다. 리소스가 사용 가능한 경우 절전 모드가 호출되지 않습니다. 그러나 리소스가 차단되면 적절한 시간 동안 잠자고 리소스에 다시 액세스하려고 시도합니다 (이로 인해 루프가 발생합니다). 그러나 루프에 어떤 종류의 리미터를 넣어야하므로 잠재적으로 무한 루프가 아닙니다.


답변

여기에서 다루지 않은 사용 사례가 있으며 이것이 Thread.Sleep ()을 사용하는 유효한 이유라고 주장 할 것입니다.

정리 작업을 실행하는 콘솔 응용 프로그램에서 수천 명의 동시 사용자가 공유하는 DB에 대해 상당히 비싼 데이터베이스 호출을 많이해야합니다. DB를 망치지 않고 몇 시간 동안 다른 사람을 제외하려면 호출 사이에 100ms 정도의 일시 중지가 필요합니다. 이것은 타이밍과 관련이 없으며 다른 스레드를 위해 DB에 액세스하는 것과 관련이 있습니다.

서버에서 단일 인스턴스로 실행되는 스레드에 대해 1MB의 스택이있는 것처럼 실행하는 데 500ms가 걸릴 수있는 호출 간의 컨텍스트 전환에 2000-8000 사이클을 소비하는 것은 무해합니다.


답변

나는 여기에있는 많은 사람들에게 동의하지만 나 또한 그것이 다르다고 생각합니다.

최근에이 코드를 작성했습니다.

private void animate(FlowLayoutPanel element, int start, int end)
{
    bool asc = end > start;
    element.Show();
    while (start != end) {
        start += asc ? 1 : -1;
        element.Height = start;
        Thread.Sleep(1);
    }
    if (!asc)
    {
        element.Hide();
    }
    element.Focus();
}

그것은 단순한 애니메이션 기능이었고 저는 그것을 사용 Thread.Sleep했습니다.

내 결론은, 그것이 효과가 있다면 그것을 사용하십시오.