C#

C#의 스레드

기하 2021. 8. 30. 02:12

C# threading을 사용하면 개발자가 C# 및 .NET에서 여러 스레드를 만들 수 있습니다. 
이 문서 및 코드 예제에서는 .NET 및 C#에서 스레드를 사용하방법을 배우고

첫 번째 스레딩 앱을 만들어 볼 것입니다.

 

새 프로그램이 Windows에서 시작되면

프로세스 ID가 있는 응용 프로그램에 대한 프로세스가 생성되고 일부 리소스가 이 새 프로세스에 할당됩니다. 

모든 프로세스에는 애플리케이션 실행의 진입점을 처리하는 하나 이상의 기본 스레드가 있습니다. 

단일 스레드는 하나의 실행 경로만 가질 수 있지만

앞서 언급한 것처럼 때로는 여러 실행 경로가 필요할 수 있으며 여기서 스레드가 역할을 합니다.

 

.NET Core에서 CLR(공용 언어 런타임)은 스레드 수명 주기를 만들고 관리하는 데 중요한 역할을 합니다. 

새 .NET Core 애플리케이션에서 CLR은 Main 메서드를 통해 애플리케이션 코드를 실행하는

단일 포그라운드 스레드를 만듭니다. 이 스레드를 기본 스레드 또는 메인 스레드라고 합니다. 

 

이 메인 스레드와 함께 프로세스는 코드의 일부를 실행하기 위해 하나 이상의 스레드를 생성할 수 있습니다. 

또한 프로그램은 ThreadPool 클래스를 사용하여 CLR에서 관리하는 작업자 스레드에서 코드를 실행할 수 있습니다.

 

C# 프로그램은 설계상 단일 스레드입니다. 

즉, 주 또는 기본 스레드는 한 번에 하나의 코드 경로만 실행합니다. 

C# 프로그램의 진입점은 Main 메서드에서 시작하며 이는 기본 스레드의 경로입니다.

왜 스레드인가? 

Main 메서드는 C# 프로그램의 진입점입니다. 

Main 메서드의 코드는 단일 기본 스레드에서 선형 방식으로 실행됩니다.

Listing 1에 있는 코드의 예를 살펴보겠습니다. 

using System;  
  
class Program  
{  
    // This is the entry point of a C# program  
    static void Main(string[] args)  
    {  
        // Main execution starts here  
        Console.WriteLine("Main thread starts here.");  
  
        // This method takes 4 seconds to finish.  
        Program.DoSomeHeavyLifting();      
  
        // This method doesn't take anytime at all.  
        Program.DoSomething();  
  
        // Execution ends here  
        Console.WriteLine("Main thread ends here.");  
        Console.ReadKey();  
    }  
  
    public static void DoSomeHeavyLifting()  
    {  
        Console.WriteLine("I'm lifting a truck!!");  
        Thread.Sleep(1000);  
        Console.WriteLine("Tired! Need a 3 sec nap.");  
        Thread.Sleep(1000);  
        Console.WriteLine("1....");  
        Thread.Sleep(1000);  
        Console.WriteLine("2....");  
        Thread.Sleep(1000);  
        Console.WriteLine("3....");  
        Console.WriteLine("I'm awake.");  
    }  
    public static void DoSomething()  
    {  
        Console.WriteLine("Hey! DoSomething here!");  
        for (int i = 0; i < 20; i++)  
            Console.Write($"{i} ");  
        Console.WriteLine();  
        Console.WriteLine("I'm done.");  
    }  
}

목록 1에서 DoSomeHeavyLifting은 약 4초가 소요되는 첫 번째 호출되는 메서드입니다. 그런 다음 DoSomething 메서드가 호출됩니다. 이 경우 DoSomething 메서드는 DoSomeHeavyLifting 메서드가 실행될 때까지 기다려야 합니다. 더 긴 백그라운드 작업이 필요한 함수가 있으면 어떻게 합니까? 예를 들어 보고서를 만들고 인쇄합니다. 방법이 더 오래 걸리고 사용자가 다른 작업을 수행하기 위해 기다려야 하는 Windows 응용 프로그램은 어떻습니까?

다중 스레드 또는 다중 스레드가 이 문제에 대한 솔루션입니다. 멀티스레딩 또는 간단히 스레딩을 사용하면 시간이 많이 소요되는 백그라운드 작업을 실행하는 데 사용할 수 있는 보조 스레드를 만들고 기본 스레드를 기본 프로그램에서 사용할 수 있도록 남겨 둘 수 있습니다. 이렇게 하면 응용 프로그램의 응답 속도가 빨라지고 사용하기 쉽습니다.

이제 Main 메서드의 DoSomeHeavyLifting 메서드 호출을 새 스레드를 만드는 새 코드로 교체해 보겠습니다. 다음 코드를 교체합니다.

  1. Program.DoSomeHeavyLifting();  

다음 코드로,

  1. // 스레드를 생성하고 백그라운드 메소드 호출   
  2. 스레드 backgroundThread =  new  Thread( new  ThreadStart(Program.DoSomeHeavyLifting));  
  3. // 스레드 시작  
  4. backgroundThread.Start();  

이 코드에서는 ThreadStart 대리자를 백그라운드에서 실행되는 메서드와 함께 매개 변수로 사용하는 Thread 클래스를 사용하여 새 스레드 개체를 만들었습니다. Thread.Start() 메서드는 새 스레드에 별표를 표시합니다. 이 새 스레드를 작업자 스레드 또는 보조 스레드라고 합니다.

이제 작업자 스레드 메서드가 얼마나 오래 걸리든 메인 스레드 코드가 나란히 실행됩니다.

새 Main 메서드는 목록 2에 나열되어 있습니다.

  1. 정적 무효  메인(문자열[] 인수)   
  2. {  
  3.     // 메인 실행은 여기에서 시작됩니다.  
  4.     Console.WriteLine( "메인 스레드는 여기에서 시작됩니다." );  
  5.   
  6.     // 쓰레드 생성   
  7.     스레드 backgroundThread =  new  Thread( new  ThreadStart(Program.DoSomeHeavyLifting));  
  8.     // 스레드 시작  
  9.     backgroundThread.Start();          
  10.   
  11.     // 이 방법은 전혀 시간이 걸리지 않습니다.  
  12.     프로그램.DoSomething();  
  13.   
  14.     // 여기서 실행 종료  
  15.     Console.WriteLine( "메인 스레드는 여기서 끝납니다." );  
  16.     콘솔.ReadKey();  
  17. }  

목록 2.

이제 프로그램을 실행하면 DoSomething 메서드 실행에 지연이 없음을 알 수 있습니다.

스레드, 리소스 및 성능 

더 많은 스레드를 생성하는 것은 처리 속도나 성능과 관련이 없습니다. 모든 스레드는 시스템이 가지고 있는 동일한 프로세서와 리소스를 공유합니다. 스레드가 여러 개인 경우 운영 체제의 도움을 받아 스레드 스케줄러가 스레드를 스케줄링하고 각 스레드에 시간을 할당합니다. 그러나 단일 프로세서 시스템에서는 한 번에 하나의 스레드만 실행할 수 있습니다. 나머지 스레드는 프로세서를 사용할 수 있을 때까지 기다려야 합니다. 단일 프로세서 시스템에서 몇 개 이상의 스레드를 생성하면 제대로 관리되지 않으면 리소스 병목 현상이 발생할 수 있습니다. 이상적으로는 프로세서당 몇 개의 스레드가 필요합니다. 듀얼 코어 프로세서의 경우 4개의 스레드가 이상적입니다. 쿼드 코어 프로세서의 경우 문제를 눈치채지 않고 최대 8개의 스레드를 생성할 수 있습니다.

스레드 리소스 관리는 리소스가 부족한 앱에서도 매우 중요합니다. 백그라운드 IO 처리와 백그라운드 데이터베이스 작업이 있는 경우 각 스레드가 별도의 리소스와 함께 작동하도록 관리해야 합니다.

프로세스와 마찬가지로 스레드도 자체 경계 내에서 실행되지만 서로 통신하고 리소스를 공유하며 데이터를 전달할 수 있습니다.

C#에서 스레드 생성 및 시작 

Thread 클래스는 스레드를 나타내며 스레드의 수명 주기와 상태, 우선 순위, 상태와 같은 속성을 만들고 관리하는 기능을 제공합니다.

Thread 클래스는 스레딩 관련 형식을 사용하기 전에 가져와야 하는 System.Threading 네임스페이스에 정의되어 있습니다.

  1. System.Threading 사용;  

Thread 생성자는 ThreadStart 대리자를 매개 변수로 사용하여 새 스레드를 만듭니다. ThreadStart의 매개변수는 새 스레드에 의해 실행되는 메소드입니다. 스레드가 생성되면 실제로 스레드를 시작하려면 Start 메서드를 호출해야 합니다.

다음 코드 조각은 Print 메서드에서 코드를 실행할 새 스레드 workerThread를 만듭니다.

  1. // ThreadStart 대리자를 전달하여 보조 스레드를 만듭니다.  
  2. 스레드 작업자 스레드 =  new  Thread( new  ThreadStart(인쇄));  
  3. // 보조 스레드 시작  
  4. 작업자 스레드. 시작();  

일부 백그라운드 또는 전경 작업을 수행하는 코드를 실행하는 데 사용할 수 있는 Print 메서드가 아래에 나열되어 있습니다.

  1. 정적 무효  Print()   
  2. {  
  3.       
  4. }  

해 보자.

비주얼 스튜디오를 엽니다. 새 .NET Core 콘솔 프로젝트를 만듭니다. 모든 코드를 삭제하고 목록 1의 코드를 복사하여 붙여넣습니다(또는 입력).

  1. 시스템 사용;  
  2. System.Threading 사용;  
  3.   
  4. 수업  프로그램  
  5. {  
  6.     정적 무효  메인()   
  7.     {  
  8.         // ThreadStart 대리자를 전달하여 보조 스레드를 만듭니다.  
  9.         스레드 작업자 스레드 =  new  Thread( new  ThreadStart(인쇄));  
  10.         // 보조 스레드 시작  
  11.         작업자 스레드. 시작();  
  12.   
  13.         // 메인 쓰레드 : 0.2초마다 1~10을 출력한다.   
  14.         // Thread.Sleep 메소드는 현재 스레드를 잠자기 상태로 만드는 역할을 합니다.  
  15.         // 밀리초 단위. 잠자기 동안 스레드는 아무 작업도 수행하지 않습니다.  
  16.         for  ( int  i=0; i< 10; i++)  
  17.         {  
  18.             Console.WriteLine($ "메인 스레드: {i}" );  
  19.             Thread.Sleep(200);  
  20.         }         
  21.   
  22.         콘솔.ReadKey();  
  23.     }  
  24.   
  25.     /// <요약>  
  26.     /// 이 코드는 보조 스레드에 의해 실행됩니다.  
  27.     /// </요약>  
  28.     정적 무효  Print()   
  29.     {  
  30.         for  ( int  i = 11; i < 20; i++)  
  31.         {  
  32.             Console.WriteLine($ "작업자 스레드: {i}" );  
  33.             Thread.Sleep(1000);  
  34.         }  
  35.     }  
  36. }  

목록 1.

Listing 1의 코드에서 메인 스레드는 0.2초마다 1에서 10을 출력합니다. 보조 스레드는 1.0초마다 11에서 20까지 인쇄합니다. 데모 목적으로 지연을 사용하고 있으므로 두 스레드가 코드를 병렬로 실행하는 방법을 실시간으로 볼 수 있습니다.

출력은 그림 1과 같습니다. 여기서 주 스레드는 0.2초마다 숫자를 인쇄하고 보조 스레드는 1.0초마다 숫자를 인쇄하고 두 스레드가 병렬로 실행되는 것을 볼 수 있습니다.


그림 1. 

스레드 이름, 스레드 우선 순위 및 스레드 상태 

Name 및 Priority 속성을 사용하여 스레드의 이름과 우선 순위를 설정할 수 있습니다. 다음 코드 조각은 스레드의 이름을 설정합니다. 

  1. workerThread.Name =  "열심히 일하는 사람" ;  

Priority 속성은 스레드의 우선 순위를 가져오고 설정하는 데 사용됩니다. 운영 체제는 우선 순위가 낮은 스레드보다 우선 순위가 높은 스레드를 실행합니다. Priority 속성은 ThreadPriority 열거형 유형입니다. ThreadPriority 값은 Highest, AboveNormal, Normal, UnderNormal 및 Lowest입니다. 다음 코드 조각은 스레드 우선 순위를 가장 높게 설정합니다.

  1. workerThread.Priority = ThreadPriority.Highest;  

스레드 상태 

수명 주기 동안 각 스레드는 상태를 거칩니다. 다음 다이어그램은 다양한 스레드 상태를 보여줍니다. 스레드는 항상 Unstrated 상태이며 유일한 전환은 스레드를 시작하고 상태가 Running으로 변경되는 것입니다. 실행 중인 스레드에는 Suspended, Sleep 및 AbortRequested의 세 가지 전환이 있습니다. 더 많은 상태는 아래 표에 설명되어 있습니다.

ThreadState 속성은 스레드의 현재 상태를 반환합니다. 이 속성을 사용하여 스레드의 상태를 설정할 수 없습니다. 표 1은 다양한 스레드 상태를 설명합니다.

상태 설명
쓰레드가 생성된다 시작되지 않음
다른 스레드가 새 스레드에서 Thread.Start 메서드를 호출하고 호출이 반환됩니다. 새 스레드가 실행을 시작할 때까지 Start 메서드는 반환되지 않습니다. Start를 호출하는 동안 새 스레드가 실행을 시작할 시점을 알 수 있는 방법이 없습니다. 달리기
스레드가 Sleep을 호출합니다. WaitSleepJoin
스레드는 다른 개체에서 Wait를 호출합니다. WaitSleepJoin
스레드는 다른 스레드에서 Join을 호출합니다. WaitSleepJoin
다른 스레드가 Interrupt를 호출합니다. 달리기
다른 스레드가 Suspend를 호출합니다. 일시 중단 요청됨
스레드는 일시 중단 요청에 응답합니다. 정지 된
다른 스레드가 Resume을 호출합니다. 달리기
다른 스레드가 Abort를 호출합니다. 중단 요청됨
스레드는 중단 요청에 응답합니다. 중지됨
스레드가 종료됩니다. 중지됨
스레드 상태는 AbortRequested를 포함하고 스레드는 이제 죽었지만 해당 상태는 아직 Stopped로 변경되지 않았습니다. 중단됨
스레드가 백그라운드 스레드인 경우 배경

1 번 테이블.

스레드는 한 번에 둘 이상의 상태에 있을 수 있습니다.

다음 코드 조각은 스레드 상태가 실행 중인지 확인한 다음 중단합니다.

  1. // 스레드 상태 확인   
  2. if  (workerThread.ThreadState == ThreadState.Running)  
  3. {  
  4.     // 실행 중이면 중단합니다.  
  5.     작업자 스레드. 중단();  
  6. }  

C#에서 현재 스레드 가져오기 

Thread.CurrentThread는 현재 코드를 실행 중인 현재 스레드를 반환합니다. 다음 코드 조각은 ID, 우선 순위, 이름 및 문화와 같은 현재 스레드의 속성을 인쇄합니다. 

  1. 스레드 currentThread = Thread.CurrentThread;  
  2. 스레드 currentThread = Thread.CurrentThread;  
  3. Console.WriteLine( "스레드 ID {0}: " , currentThread.ManagedThreadId);  
  4. Console.WriteLine( "스레드 배경: {0}" , currentThread.IsBackground);  
  5. Console.WriteLine( "우선순위: {0}" , currentThread.Priority);  
  6. Console.WriteLine( "문화: {0}" , currentThread.CurrentCulture.Name);  
  7. Console.WriteLine( "UI 문화: {0}" , currentThread.CurrentUICulture.Name);  
  8. Console.WriteLine();  

스레드는 현재 시스템에서 문화 및 UI 문화를 상속합니다.

C#의 전경 및 배경 스레드 

스레드에는 전경과 배경의 두 가지 유형이 있습니다. 기본 응용 프로그램 스레드 외에 Thread 클래스 생성자를 호출하여 생성된 모든 스레드는 전경 스레드입니다.

백그라운드 스레드는 런타임에서 유지 관리하는 작업자 스레드의 풀인 ThreadPool에서 생성되고 사용되는 스레드입니다. 백그라운드 스레드는 한 가지 예외를 제외하고 전경 스레드와 동일합니다. 모든 전경 스레드가 종료된 경우 백그라운드 스레드는 프로세스를 계속 실행하지 않습니다. 모든 포그라운드 스레드가 중지되면 런타임은 모든 백그라운드 스레드를 중지하고 종료됩니다.

언제든지 IsBackground 속성을 설정하여 스레드가 백그라운드에서 실행되도록 변경할 수 있습니다. 백그라운드 스레드는 응용 프로그램이 실행되는 동안 계속되어야 하지만 파일 시스템 변경 모니터링이나 소켓 연결 모니터링과 같이 응용 프로그램이 종료되는 것을 방지해서는 안 되는 모든 작업에 유용합니다.

스레드 일시 중지 및 중단 

Thread.Sleep 메서드를 사용하여 밀리초 단위의 고정된 기간 동안 현재 스레드를 일시 중지할 수 있습니다. 다음 코드 스니펫은 스레드를 1초 동안 일시 중지합니다. 

  1. Thread.Sleep(1000);  

Abort 메서드는 스레드를 중단하는 데 사용됩니다. Abort 전에 IsAlive를 호출해야 합니다.

  1. if  (workerThread.IsAlive)  
  2.     작업자 스레드. 중단();   

작업자 스레드에 데이터 전달 

일부 데이터는 기본 스레드에서 작업자 스레드로 전달되어야 하는 경우가 많습니다. Thread.Start 메서드에는 개체 형식 매개 변수를 사용하는 오버로드된 형식이 있습니다.

Thread 클래스 생성자는 ThreadStart 또는 ParemeterizedThreadStart 대리자를 사용합니다. ParemeterizedThreadStart 대리자는 스레드에 전달해야 할 때 사용됩니다.

Print 클래스에 PrintJob 및 PrintPerson의 두 가지 메서드가 있는 다음 코드 조각을 살펴보겠습니다. 두 개의 개별 작업자 스레드에서 이러한 메서드를 실행하려고 합니다. 분명히 알 수 있듯이 두 메서드 모두 개체 유형의 매개 변수를 사용합니다. 

  1. 공개 클래스  인쇄   
  2. {  
  3.     공개 무효  PrintJob(객체 데이터)   
  4.     {  
  5.         Console.WriteLine( "백그라운드 인쇄 작업이 시작되었습니다." );  
  6.         Thread.Sleep(1000);  
  7.         Console.WriteLine( "PrintJob이 아직 인쇄 중입니다..." );  
  8.         Console.WriteLine($ "데이터: {데이터}" );  
  9.         Thread.Sleep(1000);  
  10.         Console.WriteLine( "인쇄 작업이 완료되었습니다." );  
  11.     }  
  12.   
  13.     public void  PrintPerson(객체 데이터)   
  14.     {  
  15.         Console.WriteLine( "배경 PrintPerson이 시작되었습니다." );  
  16.         Thread.Sleep(1000);  
  17.         Console.WriteLine( "PrintPerson이 아직 인쇄 중입니다..." );  
  18.         사람 p = (사람) 데이터;  
  19.         Console.WriteLine($ "Person {p.Name}은(는) {p.Age} 연령의 {p.Sex}입니다." );  
  20.         Thread.Sleep(1000);  
  21.         Console.WriteLine( "PrintPerson이 완료되었습니다." );  
  22.     }  
  23. }  

이제 메인 스레드에서 작업자 스레드로 객체 유형을 전달하는 방법을 살펴보겠습니다.

다음 코드 조각은 PrintJob 메서드를 실행하는 새 작업자 스레드를 시작합니다. PrintJob은 ParemeterizedThreadStart 대리자이므로 Start 메서드는 매개 변수를 사용합니다. 이 경우 문자열을 매개변수로 전달합니다.

  1. // ParemeterizedThreadStart를 사용하여 스레드 생성  
  2. 인쇄 p =  new  Print();  
  3. 스레드 작업자 스레드 =  새로운  스레드(p.PrintJob);  
  4. // 매개변수로 스레드 시작  
  5. workerThread.Start( "일부 데이터" );  

Print 클래스의 PrintPerson 메서드는 Person 유형의 복합 개체를 사용합니다. 다음 코드 조각에서 Person 클래스를 만들고 Thread.Start 메서드의 매개 변수로 전달합니다.

  1. // 작업자 스레드에 클래스 객체 전달  
  2. Person mahesh =  new  Person( "Mahesh Chand" , 40,  "남성" );  
  3. 스레드 작업자Thread2 =  새로운  스레드(p.PrintPerson);  
  4. workerThread2.Start(마헤시);  

ParameterizedThreadStart 대리자는 단일 매개 변수만 지원합니다. 복잡하거나 여러 데이터 항목을 전달하려면 Array, 컬렉션 유형 또는 튜플 유형을 전달할 수 있습니다.

스레드 풀 

새로운 쓰레드의 생성과 소멸은 비용과 함께 애플리케이션의 성능에 영향을 미칩니다. 스레드는 차단되거나 절전 모드 또는 기타 해결되지 않은 상태가 될 수도 있습니다. 앱이 워크로드를 적절하게 분배하지 않으면 작업자 스레드가 대부분의 시간을 잠자기 상태로 보낼 수 있습니다. 여기서 스레드 풀이 유용합니다.

스레드 풀은 이미 생성되어 앱이 필요에 따라 사용할 수 있는 작업자 스레드의 풀입니다. 스레드 풀 스레드가 작업 실행을 마치면 풀로 돌아갑니다.

.NET은 시스템에서 관리하는 ThreadPool 클래스를 통해 관리되는 스레드 풀을 제공합니다. 개발자로서 우리는 스레드 관리 오버헤드를 다룰 필요가 없습니다. 짧은 백그라운드 작업의 경우 관리되는 스레드 풀이 자체 스레드를 만들고 관리하는 것보다 더 나은 선택입니다. 스레드 풀 스레드는 백그라운드 프로세스에만 적합하며 포그라운드 스레드에는 권장되지 않습니다. 프로세스당 하나의 스레드 풀만 있습니다.

다음과 같은 경우 스레드 풀을 사용하지 않는 것이 좋습니다.

  • 스레드의 우선 순위를 지정해야 합니다.
  • 스레드는 전경 스레드입니다.
  • 스레드를 오랫동안 차단하는 작업이 있습니다. 스레드 풀에는 최대 스레드 수가 있으므로 차단된 스레드 풀 스레드가 많으면 작업이 시작되지 않을 수 있습니다.
  • 스레드를 단일 스레드 아파트에 배치해야 합니다. 모든 ThreadPool 스레드는 다중 스레드 아파트에 있습니다.
  • 스레드와 연결된 안정적인 ID가 필요하거나 스레드를 작업에 전용으로 할당해야 합니다. 

C# 및 .NET Core의 스레드 풀 자세히 알아보기 

스레드 풀 스레드 

ThreadPool 클래스에는 사용 가능한 경우 스레드 풀 작업자 스레드를 호출하는 QueueUserWorkItem을 비롯한 여러 정적 메서드가 있습니다. 스레드 풀에서 사용할 수 있는 작업자 스레드가 없으면 스레드를 사용할 수 있을 때까지 기다립니다.

QueueWorkItem 메서드는 백그라운드에서 실행되는 프로시저를 사용합니다.

  1. ThreadPool.QueueUserWorkItem(배경 작업);  

다음은 스레드 풀에서 작업자 스레드를 호출하여 백그라운드에서 메서드를 실행하는 방법에 대한 완전한 예입니다.

  1. 시스템 사용;  
  2. System.Threading 사용;  
  3.   
  4. 클래스  ThreadPoolSample  
  5. {  
  6.     // 백그라운드 작업   
  7.     정적 무효  BackgroundTask(객체 stateInfo)   
  8.     {  
  9.         Console.WriteLine( "안녕하세요! 저는 ThreadPool의 작업자입니다 . " );  
  10.         Thread.Sleep(1000);          
  11.     }  
  12.   
  13.     정적 무효  메인(문자열[] 인수)   
  14.     {  
  15.         // 작업자 스레드에 ThreadPool 사용        
  16.         ThreadPool.QueueUserWorkItem(배경 작업);  
  17.         Console.WriteLine( "메인 스레드가 일부 작업을 수행한 후 절전 모드로 전환됩니다." );  
  18.         Thread.Sleep(500);  
  19.         Console.WriteLine( "메인 스레드가 종료됩니다." );  
  20.         콘솔.ReadKey();  
  21.     }  
  22. }  

QueueWorkItem 메서드를 통해 백그라운드 메서드에 값을 전달할 수도 있습니다. 메서드의 두 번째 매개 변수는 백그라운드 프로시저에 전달하려는 모든 개체가 될 수 있는 개체입니다.

다음 멤버가 있는 Person 클래스가 있다고 가정해 보겠습니다.

  1. // Person 클래스 생성  
  2. 공개 클래스  사람   
  3. {  
  4.     공개  문자열 이름 { get; 세트; }  
  5.     공개 정수  연령 { get; 세트; }   
  6.     공개  문자열 섹스 { get; 세트; }  
  7.   
  8.     public  Person(문자열 이름,  정수  연령, 문자열 성별)  
  9.     {  
  10.          .이름 = 이름;  
  11.          .Age = 나이;  
  12.          .Sex = 섹스;  
  13.     }  
  14. }  

Person 유형의 개체를 만들어 QueueUserWorkItem 메서드에 전달할 수 있습니다.

  1. // 객체를 생성하여 ThreadPool 작업자 스레드에 전달  
  2. Person p =  new  Person( "Mahesh Chand" , 40,  "남성" );  
  3. ThreadPool.QueueUserWorkItem(BackgroundTaskWithObject, p);  

그리고 작업자 메서드에서는 객체에서 값을 추출하여 사용할 수 있습니다. 다음 예에서는 Person.Name을 다시 읽고 콘솔에 표시합니다.

  1. 정적 무효  BackgroundTaskWithObject(객체 상태 정보)   
  2. {  
  3.     개인 데이터 = (Person)stateInfo;          
  4.     Console.WriteLine($ "안녕하세요 ThreadPool의 {data.Name}." );  
  5.     Thread.Sleep(1000);  
  6. }  

전체 코드는 목록 2에 나열되어 있습니다.

  1. 시스템 사용;  
  2. System.Threading 사용;  
  3.   
  4. 클래스  ThreadPoolSample  
  5. {  
  6.     // 백그라운드 작업   
  7.     정적 무효  BackgroundTask(객체 stateInfo)   
  8.     {  
  9.         Console.WriteLine( "안녕하세요! 저는 ThreadPool의 작업자입니다 . " );  
  10.         Thread.Sleep(1000);          
  11.     }  
  12.   
  13.     정적 무효  BackgroundTaskWithObject(객체 상태 정보)   
  14.     {  
  15.         개인 데이터 = (Person)stateInfo;          
  16.         Console.WriteLine($ "안녕하세요 ThreadPool의 {data.Name}." );  
  17.         Thread.Sleep(1000);  
  18.     }  
  19.     정적 무효  메인(문자열[] 인수)   
  20.     {  
  21.         // 객체를 생성하여 ThreadPool 작업자 스레드에 전달  
  22.         Person p =  new  Person( "Mahesh Chand" , 40,  "남성" );  
  23.         ThreadPool.QueueUserWorkItem(BackgroundTaskWithObject, p);  
  24.   
  25.         콘솔.ReadKey();  
  26.     }  
  27.   
  28.         // Person 클래스 생성  
  29.         공개 클래스  사람   
  30.         {  
  31.             공개  문자열 이름 { get; 세트; }  
  32.             공개 정수  연령 { get; 세트; }   
  33.             공개  문자열 섹스 { get; 세트; }  
  34.   
  35.             public  Person(문자열 이름,  정수  연령, 문자열 성별)  
  36.             {  
  37.                  .이름 = 이름;  
  38.                  .Age = 나이;  
  39.                  .Sex = 섹스;  
  40.             }  
  41.         }  
  42. }  

목록 2. 

최대 및 최소 스레드 풀 스레드 

스레드 풀 크기는 스레드 풀에서 사용할 수 있는 스레드 수입니다. 스레드 풀은 각 범주의 최소값에 도달할 때까지 요청 시 새 작업자 스레드 또는 I/O 완료 스레드를 제공합니다. 기본적으로 최소 스레드 수는 시스템의 프로세서 수로 설정됩니다. 최소값에 도달하면 스레드 풀은 해당 범주에 추가 스레드를 생성하거나 일부 작업이 완료될 때까지 기다릴 수 있습니다. 스레드 풀은 단위 시간당 완료되는 작업 수로 정의되는 처리량을 최적화하기 위해 스레드를 생성하고 파괴합니다. 스레드가 너무 적으면 사용 가능한 리소스를 최적으로 사용하지 못할 수 있지만 스레드가 너무 많으면 리소스 경합이 증가할 수 있습니다.

ThreadPool.GetAvailalbeThreads는 현재 풀에서 사용 가능한 스레드 수를 반환합니다. 최대 스레드 수에서 현재 활성 스레드를 뺀 값입니다.

  1. int  작업자, 포트;  
  2. // 사용 가능한 스레드 가져오기  
  3. ThreadPool.GetAvailableThreads(작업자 출력, 포트 출력);  
  4. Console.WriteLine($ "사용 가능한 작업자 스레드: {workers} " );  
  5. Console.WriteLine($ "사용 가능한 완료 포트 스레드: {ports}" );  

ThreadPool.GetMaxThreads 및 ThreadPool.GetMinThreads는 스레드 풀에서 사용 가능한 최대 및 최소 스레드를 반환합니다.

  1. int  작업자, 포트;  
  2. // 최대 스레드 수 얻기  
  3. ThreadPool.GetMaxThreads(작업자 출력, 포트 출력);  
  4. Console.WriteLine($ "최대 작업자 스레드: {workers} " );  
  5. Console.WriteLine($ "최대 완료 포트 스레드: {ports}" );  

ThreadPool.SetMaxThreads 및 ThreadPool.SetMinThreads는 스레드 풀에서 필요에 따라 요청 시 최대 및 최소 스레드 수를 설정하는 데 사용됩니다. 기본적으로 최소 스레드 수는 시스템의 프로세서 수로 설정됩니다.

  1. int  작업자, 포트;  
  2. // 최소 스레드 설정  
  3. int  minWorker, minIOC;  
  4. ThreadPool.GetMinThreads(minWorker 출력, minIOC 출력);  
  5. ThreadPool.SetMinThreads(4, minIOC);  

전체 예제는 목록 3에 나열되어 있습니다.

  1. 시스템 사용;  
  2. System.Threading 사용;  
  3.   
  4. 클래스  ThreadPoolSample  
  5. {  
  6.     // 백그라운드 작업   
  7.     정적 무효  BackgroundTask(객체 stateInfo)   
  8.     {  
  9.         Console.WriteLine( "안녕하세요! 저는 ThreadPool의 작업자입니다 . " );  
  10.         Thread.Sleep(1000);          
  11.     }  
  12.   
  13.     정적 무효  BackgroundTaskWithObject(객체 상태 정보)   
  14.     {  
  15.         개인 데이터 = (Person)stateInfo;          
  16.         Console.WriteLine($ "안녕하세요 ThreadPool의 {data.Name}." );  
  17.         Thread.Sleep(1000);  
  18.     }  
  19.     정적 무효  메인(문자열[] 인수)   
  20.     {  
  21.         // 작업자 스레드에 ThreadPool 사용        
  22.         ThreadPool.QueueUserWorkItem(배경 작업);  
  23.         Console.WriteLine( "메인 스레드가 일부 작업을 수행한 후 절전 모드로 전환됩니다." );  
  24.         Thread.Sleep(500);  
  25.      
  26.         // 객체를 생성하여 ThreadPool 작업자 스레드에 전달  
  27.         Person p =  new  Person( "Mahesh Chand" , 40,  "남성" );  
  28.         ThreadPool.QueueUserWorkItem(BackgroundTaskWithObject, p);  
  29.   
  30.         int  작업자, 포트;  
  31.   
  32.         // 최대 스레드 수 얻기  
  33.         ThreadPool.GetMaxThreads(작업자 출력, 포트 출력);  
  34.         Console.WriteLine($ "최대 작업자 스레드: {workers} " );  
  35.         Console.WriteLine($ "최대 완료 포트 스레드: {ports}" );  
  36.       
  37.         // 사용 가능한 스레드 가져오기  
  38.         ThreadPool.GetAvailableThreads(작업자 출력, 포트 출력);  
  39.         Console.WriteLine($ "사용 가능한 작업자 스레드: {workers} " );  
  40.         Console.WriteLine($ "사용 가능한 완료 포트 스레드: {ports}" );  
  41.   
  42.         // 최소 스레드 설정  
  43.         int  minWorker, minIOC;  
  44.         ThreadPool.GetMinThreads(minWorker 출력, minIOC 출력);  
  45.         ThreadPool.SetMinThreads(4, minIOC);  
  46.   
  47.         // 머신에서 사용 가능한 총 프로세스 수를 가져옵니다.  
  48.         int  processCount = Environment.ProcessorCount;  
  49.         Console.WriteLine($ "시스템에서 사용할 수 있는 프로세스 수: {processCount}" );  
  50.   
  51.         // 최소 스레드 수 얻기  
  52.         ThreadPool.GetMinThreads(작업자 출력, 포트 출력);  
  53.         Console.WriteLine($ "최소 작업자 스레드: {workers} " );  
  54.         Console.WriteLine($ "최소 완료 포트 스레드: {ports}" );  
  55.   
  56.         콘솔.ReadKey();  
  57.     }  
  58.   
  59.         // Person 클래스 생성  
  60.         공개 클래스  사람   
  61.         {  
  62.             공개  문자열 이름 { get; 세트; }  
  63.             공개 정수  연령 { get; 세트; }   
  64.             공개  문자열 섹스 { get; 세트; }  
  65.   
  66.             public  Person(문자열 이름,  정수  연령, 문자열 성별)  
  67.             {  
  68.                  .이름 = 이름;  
  69.                  .Age = 나이;  
  70.                  .Sex = 섹스;  
  71.             }  
  72.         }  
  73. }  

목록 3.

요약 

이 기사는 .NET의 스레딩에 대한 소개입니다. 이 기사에서는 스레딩이 유용한 이유와 백그라운드 작업을 실행하기 위해 작업자 스레드를 만들고 관리하는 방법을 배웠습니다. 또한 관리되는 스레드 풀과 이를 사용하여 백그라운드 작업을 실행하는 방법에 대해서도 배웠습니다.

 

[출처] https://www.c-sharpcorner.com/article/Threads-in-CSharp/

'C#' 카테고리의 다른 글

C# 시리얼 통신 클래스 만들기  (0) 2023.01.31
Serial Port Programming With .NET  (0) 2023.01.26
Find The Difference Of Day, Hour, Minutes, Seconds, In C#  (0) 2021.08.30