Microsoft MVP성태의 닷넷 이야기
.NET Framework: 1195. C# - Thread.Yield와 Thread.Sleep(0)의 차이점(?) [링크 복사], [링크+제목 복사],
조회: 7644
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)
(시리즈 글이 4개 있습니다.)
.NET Framework: 914. C# - Task.Yield 사용법
; https://www.sysnet.pe.kr/2/0/12241

.NET Framework: 916. C# - Task.Yield 사용법 (2)
; https://www.sysnet.pe.kr/2/0/12245

.NET Framework: 1163.  C# - 윈도우 환경에서 usleep을 호출하는 방법
; https://www.sysnet.pe.kr/2/0/12980

.NET Framework: 1195. C# - Thread.Yield와 Thread.Sleep(0)의 차이점(?)
; https://www.sysnet.pe.kr/2/0/13033




C# - Thread.Yield와 Thread.Sleep(0)의 차이점(?)

예전 글에서,

(번역글) .NET Internals Cookbook Part 10 - Threads, Tasks, asynchronous code and others
67. Thread.Yield와 Thread.Sleep(0)의 차이점
; https://www.sysnet.pe.kr/2/0/11879#67

이에 대해 설명을 했는데, 그때 테스트가 잘못돼 다시 설명을 합니다. ^^;




자, 우선, Yield는 "현재 프로세서에 ready 상태의 스레드가 있는지 체크 후 있으면 해당 스레드로 전환이 되지만 없으면 현재 스레드가 계속 실행"하는 것을 테스트해보겠습니다.

이를 위해 다음과 같이 코드를 작성하고,

using System;
using System.Diagnostics;
using System.Numerics;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static int _processorId = 1;

        static void Main(string[] args)
        {
            Thread t1 = new Thread(yieldProc);
            Thread t2 = new Thread(lowPriorityProc);
            Thread t3 = new Thread(lowPriorityProc);

            t3.Start();
            t2.Start();
            t1.Start();
            t1.Join();
            t2.Join();
        }

        private static void lowPriorityProc()
        {
            SetProcessAffinity(_processorId);

            Console.WriteLine("lowPriorityProc: " + AppDomain.GetCurrentThreadId());
            Thread.CurrentThread.Priority = ThreadPriority.Lowest;

            int i = 0;
            BigInteger sum = new BigInteger();

            while (true)
            {
                i++;
                sum += i;
            }
        }

        private static void yieldProc()
        {
            SetProcessAffinity(_processorId);

            Console.WriteLine("yieldProc: " + AppDomain.GetCurrentThreadId());
            while (true)
            {
                Thread.Yield();
            }
        }

        // https://www.sysnet.pe.kr/2/0/10933
        static void SetProcessAffinity(int cpuNumber)
        {
            if (cpuNumber >= Environment.ProcessorCount)
            {
                cpuNumber = 0;
            }

            foreach (ProcessThread pthread in Process.GetCurrentProcess().Threads)
            {
                if (pthread.Id == AppDomain.GetCurrentThreadId()) // .NET Framework에서만!
                {
                    pthread.ProcessorAffinity = new IntPtr(1 << cpuNumber);
                    break;
                }
            }
        }
    }
}

실행하면 이런 결과가 나올 텐데요,

lowPriorityProc: 65544
lowPriorityProc: 21836
yieldProc: 45984

이때 Process Explorer를 이용해 "yieldProc"으로 지정된 45984 스레드를 찾아보면 CPU 값이 (24 코어에서) "0.01" 정도로 나오는 것을 확인할 수 있습니다. 왜냐하면, 같은 CPU에 실행 중인 lowPriorityProc 스레드 2개가 더 있기 때문에 Yield는 그 스레드로 계속해서 실행을 양보하기 때문에 (1/n도 아닌 더욱) 낮은 CPU 사용량만을 보이는 것입니다. (또한, 위의 코드에서 lowPriorityProc의 우선순위가 ThreadPriority.Lowest로 설정되었는데도 CPU 양보를 하고 있다는 것을 알 수 있습니다.)

반면, lowPriorityProc에서 SetProcessAffinity 호출을 제거하면,

private static void lowPriorityProc()
{
    // SetProcessAffinity(_processorId);

    Console.WriteLine("lowPriorityProc: " + AppDomain.GetCurrentThreadId());
    Thread.CurrentThread.Priority = ThreadPriority.Lowest;

    // ...[생략]...
}

이제는 yieldProc 스레드가 실행 중인 CPU를 lowPriorityProc 스레드에서 점유하지 않으므로, 이제 yieldProc은 CPU 100% 현상을 보이게 됩니다. 위에서 설명한 그대로 현상이 재현된 것입니다.

자, 그럼 Sleep(0)을 검증해 볼까요?

다시 위의 첫 번째 예제에서 단지 yieldProc의 내부만 Sleep 호출로 바꾼 후,

private static void yieldProc()
{
    SetProcessAffinity(_processorId);

    Console.WriteLine("yieldProc: " + AppDomain.GetCurrentThreadId());
    while (true)
    {
        // Thread.Yield();
        Thread.Sleep(0);
    }
}

실행하면, Process Explorer에서 yieldProc의 스레드는 lowPriorityProc의 스레드보다 우선순위가 높게 설정돼 있으므로 양보를 하지 말아야 합니다. 하지만, 실제로 실행해 보면 Yield처럼 낮은 CPU 사용량을 보입니다. 즉, 양보를 하고 있는 것입니다.

역시나, 이번에도 테스트상으로는 Yield와 Sleep(0)의 차이점을 알 수가 없습니다. ^^;

결국, Yield와 Sleep(0)은 경우에 따라 CPU 사용량이 (같은 CPU를 사용하는 다른 스레드가 있다면 1/n보다) 낮을 수도 있고, (같은 CPU를 사용하는 다른 스레드가 없다면) 높을 수도 있습니다. 또한 Yield/Sleep 모두, (스레드의 우선순위에 상관없이) 같은 CPU를 사용하는 다른 스레드가 있다면 1/n 사용량을 확보하진 못합니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 4/18/2022]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  37  38  39  40  [41]  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12612정성태4/23/20218967.NET Framework: 1047. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (1) PInvoke파일 다운로드1
12611정성태4/22/20218178오류 유형: 711. 닷넷 EXE 실행 오류 - Mixed mode assembly is build against version 'v2.0.50727' of the runtime
12610정성태4/22/20218011.NET Framework: 1046. C# - 컴파일 시점에 참조할 수 없는 타입을 포함한 이벤트 핸들러를 Reflection을 이용해 구독하는 방법파일 다운로드1
12609정성태4/22/20219399.NET Framework: 1045. C# - 런타임 시점에 이벤트 핸들러를 만들어 Reflection을 이용해 구독하는 방법파일 다운로드1
12608정성태4/21/202110324.NET Framework: 1044. C# - Generic Host를 이용해 .NET 5로 리눅스 daemon 프로그램 만드는 방법 [9]파일 다운로드1
12607정성태4/21/20218876.NET Framework: 1043. C# - 실행 시점에 동적으로 Delegate 타입을 만드는 방법파일 다운로드1
12606정성태4/21/202112977.NET Framework: 1042. C# - enum 값을 int로 암시적(implicit) 형변환하는 방법? [2]파일 다운로드1
12605정성태4/18/20218883.NET Framework: 1041. C# - AssemblyID, ModuleID를 관리 코드에서 구하는 방법파일 다운로드1
12604정성태4/18/20217535VS.NET IDE: 163. 비주얼 스튜디오 속성 창의 "Build(빌드)" / "Configuration(구성)"에서의 "활성" 의미
12603정성태4/16/20218445VS.NET IDE: 162. 비주얼 스튜디오 - 상속받은 컨트롤이 디자인 창에서 지원되지 않는 문제
12602정성태4/16/20219654VS.NET IDE: 161. x64 DLL 프로젝트의 컨트롤이 Visual Studio의 Designer에서 보이지 않는 문제 [1]
12601정성태4/15/20218755.NET Framework: 1040. C# - REST API 대신 github 클라이언트 라이브러리를 통해 프로그래밍으로 접근
12600정성태4/15/20218918.NET Framework: 1039. C# - Kubeconfig의 token 설정 및 인증서 구성을 자동화하는 프로그램
12599정성태4/14/20219668.NET Framework: 1038. C# - 인증서 및 키 파일로부터 pfx/p12 파일을 생성하는 방법파일 다운로드1
12598정성태4/14/20219755.NET Framework: 1037. openssl의 PEM 개인키 파일을 .NET RSACryptoServiceProvider에서 사용하는 방법 (2)파일 다운로드1
12597정성태4/13/20219831개발 환경 구성: 569. csproj의 내용을 공통 설정할 수 있는 Directory.Build.targets / Directory.Build.props 파일
12596정성태4/12/20219532개발 환경 구성: 568. Windows의 80 포트 점유를 해제하는 방법
12595정성태4/12/20218958.NET Framework: 1036. SQL 서버 - varbinary 타입에 대한 문자열의 CAST, CONVERT 변환을 C# 코드로 구현
12594정성태4/11/20218417.NET Framework: 1035. C# - kubectl 명령어 또는 REST API 대신 Kubernetes 클라이언트 라이브러리를 통해 프로그래밍으로 접근 [1]파일 다운로드1
12593정성태4/10/20219593개발 환경 구성: 567. Docker Desktop for Windows - kubectl proxy 없이 k8s 대시보드 접근 방법
12592정성태4/10/20219422개발 환경 구성: 566. Docker Desktop for Windows - k8s dashboard의 Kubeconfig 로그인 및 Skip 방법
12591정성태4/9/202112694.NET Framework: 1034. C# - byte 배열을 Hex(16진수) 문자열로 고속 변환하는 방법 [2]파일 다운로드1
12590정성태4/9/20219152.NET Framework: 1033. C# - .NET 4.0 이하에서 Console.IsInputRedirected 구현 [1]
12589정성태4/8/202110515.NET Framework: 1032. C# - Environment.OSVersion의 문제점 및 윈도우 운영체제의 버전을 구하는 다양한 방법 [1]
12588정성태4/7/202111045개발 환경 구성: 565. PowerShell - New-SelfSignedCertificate를 사용해 CA 인증서 생성 및 인증서 서명 방법
12587정성태4/6/202111868개발 환경 구성: 564. Windows 10 - ClickOnce 배포처럼 사용할 수 있는 MSIX 설치 파일 [1]
... 31  32  33  34  35  36  37  38  39  40  [41]  42  43  44  45  ...