NET에서는 System.Threading 네임스페이스에 멀티스레딩 기능을 정의합니다. 따라서 멀티스레딩을 사용하려면 먼저 이 네임스페이스에 대한 참조를 선언해야 합니다(System.Threading; 사용).
a. 스레드 시작 이름에서 알 수 있듯이 "스레드 시작"은 스레드를 만들고 시작하는 것을 의미합니다.
스레드 thread1 = new Thread(new ThreadStart(Count));
여기서 Count는 새 스레드에 의해 실행되는 함수입니다.
b. 스레드를 종료합니다.
"스레드 종료"는 스레드를 근절하는 것입니다. 노력을 낭비하지 않으려면 스레드를 종료하기 전에 IsAlive 특성을 통해 스레드가 아직 살아 있는지 확인한 다음 Abort 메서드를 호출하여 스레드를 종료하는 것이 가장 좋습니다. .
c. 스레드를 일시 중지한다는 것은 실행 중인 스레드를 일정 기간 동안 휴면 상태로 두는 것을 의미합니다. 예를 들어 thread.Sleep(1000);은 스레드를 1초 동안 휴면 상태로 두는 것입니다.
d. 우선순위에는 설명이 필요하지 않습니다. Thread 클래스의 hreadPriority 속성은 우선순위를 설정하는 데 사용되지만 운영 체제가 우선순위를 승인한다는 보장은 없습니다. 스레드의 우선순위는 Normal, AboveNormal, BelowNormal, Highest, Lowest의 5가지 유형으로 나눌 수 있습니다. 구체적인 구현 예는 다음과 같습니다.
thread.Priority = ThreadPriority.Highest;
e. 스레드를 일시 중단합니다.
Thread 클래스의 Suspend 메서드는 스레드가 계속 실행되기 전에 Resume이 호출될 때까지 스레드를 일시 중단하는 데 사용됩니다. 스레드가 이미 일시 중단된 경우에는 작동하지 않습니다.
if (thread.ThreadState = ThreadState.Running)
{
스레드.일시중단();
}
f. 복구 스레드는 스레드가 계속 실행될 수 있도록 일시 중단된 스레드를 재개하는 데 사용됩니다.
if (thread.ThreadState = ThreadState.Suspended)
{
스레드.이력서();
}
간단한 스레딩 기능을 설명하기 위한 예가 아래에 나열되어 있습니다. 이 예는 도움말 문서에서 가져온 것입니다.
시스템 사용;
System.Threading 사용;
// 간단한 스레딩 시나리오: 정적 메서드 실행 시작
// 두 번째 스레드에서.
공개 클래스 ThreadExample {
// ThreadProc 메서드는 스레드가 시작될 때 호출됩니다.
// 10번 반복하여 콘솔에 쓰고 결과를 얻습니다.
// 매번 시간 조각의 나머지 부분을 처리한 다음 종료됩니다.
공개 정적 무효 ThreadProc() {
for (int i = 0; i < 10; i++) {
Console.WriteLine("ThreadProc: {0}", i);
// 나머지 시간 조각을 생성합니다.
스레드.수면(0);
}
}
공개 정적 무효 Main() {
Console.WriteLine("메인 스레드: 두 번째 스레드를 시작합니다.");
// Thread 클래스의 생성자에는 ThreadStart가 필요합니다.
// 실행될 메서드를 나타내는 대리자
// thread.C#은 이 대리자 생성을 단순화합니다.
스레드 t = new Thread(new ThreadStart(ThreadProc));
// ThreadProc을 시작합니다. 단일 프로세서에서는 스레드가 가져오지 않습니다.
// 메인 스레드가 생성될 때까지의 프로세서 시간입니다.
// 차이점을 확인하기 위해 t.Start() 뒤에 오는 Thread.Sleep.
t.시작();
//스레드.슬립(0);
for (int i = 0; i < 4; i++) {
Console.WriteLine("메인 스레드: 작업을 수행합니다.");
스레드.수면(0);
}
Console.WriteLine("메인 스레드: Join()을 호출하여 ThreadProc이 끝날 때까지 기다립니다.");
t.Join();
Console.WriteLine("메인 스레드: ThreadProc.Join이 반환되었습니다. 프로그램을 종료하려면 Enter를 누르세요.");
Console.ReadLine();
}
}
이 코드는 다음과 유사한 출력을 생성합니다.
메인 스레드: 두 번째 스레드를 시작합니다.
메인 스레드: 작업을 수행하세요.
스레드프록: 0
메인 스레드: 작업을 수행하세요.
스레드프록: 1
메인 스레드: 작업을 수행하세요.
스레드프록: 2
메인 스레드: 작업을 수행하세요.
스레드프록: 3
메인 스레드: Join()을 호출하여 ThreadProc이 끝날 때까지 기다립니다.
스레드프록: 4
스레드프록: 5
스레드프록: 6
스레드프록: 7
스레드프록: 8
스레드프록: 9
메인 스레드: ThreadProc.Join이 반환되었습니다. Enter를 눌러 프로그램을 종료하세요.
Visul C#에서 System.Threading 네임스페이스는 다중 스레드 프로그래밍을 가능하게 하는 몇 가지 클래스와 인터페이스를 제공합니다. 스레드를 생성하는 방법에는 Thread, ThreadPool 및 Timer가 있습니다. 사용법을 하나씩 간략하게 소개하겠습니다.
1. 실
이는 아마도 가장 복잡한 방법이지만 스레드에 대한 다양하고 유연한 제어를 제공합니다. 먼저 해당 생성자를 사용하여 스레드 인스턴스를 만들어야 합니다. 매개 변수는 하나의 ThreadStart 대리자만 사용하여 비교적 간단합니다. public Thread(ThreadStart start) 그런 다음 Start()를 호출하여 Priority 특성을 사용할 수 있습니다. 실행 우선순위를 설정하거나 가져옵니다(열거형 ThreadPriority: Normal, Lowest, Highest, BelowNormal, AboveNormal).
다음 예에서는 먼저 두 개의 스레드 인스턴스 t1과 t2를 생성한 다음 각각 우선 순위를 설정한 다음 두 스레드를 시작합니다. 두 스레드는 출력이 다르다는 점을 제외하면 기본적으로 동일합니다. t1은 "1"이고 t2는 "2입니다. ", 그들이 출력하는 문자 수의 비율에 따라 그들이 차지하는 CPU 시간의 비율을 대략적으로 볼 수 있으며 이는 각각의 우선 순위도 반영합니다.)
정적 무효 Main(string[] args)
{
스레드 t1 = new Thread(new ThreadStart(Thread1));
스레드 t2 = new Thread(new ThreadStart(Thread2));
t1.Priority = ThreadPriority.BelowNormal;
t2.Priority = ThreadPriority.Lowest;
t1.시작();
t2.시작();
}
공개 정적 무효 Thread1()
{
for (int i = 1; i < 1000; i++)
{//루프가 실행될 때마다 "1"을 씁니다.
dosth();
Console.Write("1");
}
}
공개 정적 무효 Thread2()
{
for (int i = 0; i < 1000; i++)
{//루프가 실행될 때마다 "2"를 씁니다.
dosth();
Console.Write("2");
}
}
공개 정적 무효 dosth()
{//복잡한 작업을 시뮬레이션하는 데 사용됩니다.
for (int j = 0; j < 10000000; j++)
{
정수 a=15;
a = a*a*a*a;
}
}
위 프로그램을 실행한 결과는 다음과 같습니다.
11111111111111111111111111111111111111111121111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111112
위의 결과에서 t1 스레드가 t2보다 훨씬 더 많은 CPU 시간을 차지하는 것을 알 수 있습니다. 이는 t1과 t2의 우선순위를 모두 Normal로 설정하면 결과가 t2보다 높기 때문입니다. 다음 그림과 같습니다:
121211221212121212121212121212121212121212121212121212121212121212121
212121212121212121212121212121212121212121212121212121212121212121212
121212121212121212
위의 예에서 그 구조가 win32 작업자 스레드와 유사하다는 것을 알 수 있지만 스레드가 대리자로 호출할 함수를 사용한 다음 대리자를 매개변수로 사용하기만 하면 됩니다. 스레드 인스턴스를 구성합니다. Start()를 호출하여 시작하면 해당 함수가 호출되고 해당 함수의 첫 번째 줄부터 실행이 시작됩니다.
다음으로 스레드의 ThreadState 속성을 결합하여 스레드 제어를 이해합니다. ThreadState는 스레드의 상태를 반영하는 열거형입니다. Thread 인스턴스가 처음 생성되면 해당 ThreadState는 Unstarted입니다. Start()를 호출하여 스레드가 시작되면 해당 ThreadState는 Running이 됩니다. 일시 중지(차단)하려면 Thread를 호출하면 됩니다. Sleep() 메서드에는 두 가지 오버로드된 메서드(Sleep(int), Sleep(Timespan))가 있는데, 이는 시간을 나타내는 형식이 다를 뿐입니다. 스레드에서 이 함수가 호출되면 스레드가 차단됩니다. 일정 시간 동안(시간은 Sleep에 전달된 밀리초 또는 Timespan에 의해 결정되지만 매개 변수가 0인 경우 다른 스레드가 실행될 수 있도록 이 스레드를 일시 중단하고 스레드를 무기한 차단하려면 Infinite를 지정하는 것을 의미함) 이번에는 ThreadState가 WaitSleepJoin이 됩니다. 주목할 만한 또 다른 점은 Sleep() 함수가 정적으로 정의된다는 것입니다. ! 이는 또한 스레드 인스턴스와 함께 사용할 수 없음을 의미합니다. 즉, t1.Sleep(10)과 유사한 호출이 없습니다! 이처럼 Sleep() 함수는 자신이 "Sleep"을 해야 하는 스레드에서만 호출할 수 있고, 다른 스레드에서는 호출할 수 없는 것과 마찬가지로 언제 Sleep을 사용할지도 다른 사람이 결정할 수 없는 개인적인 문제입니다. . 그러나 스레드가 WaitSleepJoin 상태에 있고 이를 깨워야 하는 경우 스레드에 ThreadInterruptedException을 발생시키는 Thread.Interrupt 메서드를 사용할 수 있습니다. 먼저 예제를 살펴보겠습니다(Sleep 호출 메서드 참고).
정적 무효 Main(string[] args)
{
스레드 t1 = new Thread(new ThreadStart(Thread1));
t1.시작();
t1.인터럽트();
E.WaitOne();
t1.인터럽트();
t1.Join();
Console.WriteLine("t1이 끝났습니다");
}
static AutoResetEvent E = new AutoResetEvent(false);
공개 정적 무효 Thread1()
{
노력하다
{//매개변수를 보면 최대 절전 모드가 발생함을 알 수 있습니다.
Thread.Sleep(Timeout.Infinite);
}
catch(System.Threading.ThreadInterruptedException e)
{//인터럽트 핸들러
Console.WriteLine(" 첫 번째 인터럽트");
}
E.설정();
노력하다
{// 잠
Thread.Sleep(Timeout.Infinite);
}
catch(System.Threading.ThreadInterruptedException e)
{
Console.WriteLine(" 두 번째 인터럽트");
}//10초 동안 일시중지
스레드.수면(10000);
}
실행 결과는 다음과 같습니다: 첫 번째 인터럽트
두 번째 인터럽트
(10초 후)t1이 종료됩니다.
위의 예에서 Thread.Interrupt 메서드는 차단(WaitSleepJoin) 상태에서 프로그램을 깨우고 해당 인터럽트 핸들러에 들어간 다음 실행을 계속할 수 있음을 알 수 있습니다(ThreadState도 Running으로 변경됨). 함수는 다음 사항에 유의해야 합니다.
1. 이 방법은 Sleep으로 인한 차단을 깨울 수 있을 뿐만 아니라 스레드가 WaitSleepJoin 상태에 들어갈 수 있는 모든 방법(예: Wait 및 Join)에 효과적입니다. 위의 예에서 보듯이 이를 사용할 때에는 쓰레드 블로킹을 일으키는 메소드를 try 블록에 넣고, 해당 인터럽트 핸들러를 catch 블록에 넣어야 한다.
2. 특정 스레드에서 Interrupt를 호출합니다. WaitSleepJoin 상태이면 해당 인터럽트 핸들러에 들어가 실행됩니다. 현재 WaitSleepJoin 상태가 아니면 나중에 이 상태에 들어갈 때 즉시 중단됩니다. . 인터럽트가 인터럽트되기 전에 여러 번 호출되면 첫 번째 호출만 유효합니다. 이것이 위의 예에서 동기화를 사용한 이유입니다. 이는 첫 번째 인터럽트 후에 두 번째 인터럽트 호출이 호출되도록 하기 위함입니다. 그렇지 않으면 두 번째 인터럽트가 발생할 수 있습니다. 호출은 효과가 없습니다(첫 번째 인터럽트 이전에 호출된 경우). 동기화를 제거해 볼 수 있습니다. 결과는 다음과 같습니다.
위의 예제에서는 스레드가 WaitSleepJoin 상태로 전환되도록 하기 위해 동기화 개체와 Thread.Join 메서드를 사용하는 두 가지 다른 메서드도 사용합니다. Join 메서드의 사용은 상대적으로 간단합니다. 이는 다른 스레드(이 예에서는 t1)가 종료되거나 지정된 시간이 지날 때까지(시간 매개 변수도 사용하는 경우) 이 메서드를 호출하는 현재 스레드를 차단한다는 의미입니다. 조건(있는 경우)이 발생하면 즉시 WaitSleepJoin 상태를 종료하고 Running 상태로 들어갑니다. (조건은 .Join 메서드의 반환 값에 따라 결정될 수 있습니다. true이면 스레드가 종료되고, false이면 시간이 다 됐어요). Thread.Suspend 메서드를 사용하여 스레드를 일시 중단할 수도 있습니다. 스레드가 Running 상태이고 해당 스레드에서 Suspend 메서드가 호출되면 SuspendRequested 상태가 되지만 스레드가 안전 상태에 도달할 때까지 즉시 일시 중단되지는 않습니다. 스레드가 중단되고, 이 지점에서 일시 중단 상태가 됩니다. 이미 일시 중단된 스레드에서 호출하면 유효하지 않습니다. 작업을 재개하려면 Thread.Resume을 호출하면 됩니다.
마지막으로 이야기할 것은 스레드 소멸에 관한 것입니다. 소멸되어야 하는 스레드에서 Abort 메서드를 호출하면 이 스레드에서 ThreadAbortException이 발생합니다. 스레드의 일부 코드를 try 블록에 넣고 해당 처리 코드를 해당 catch 블록에 넣을 수 있습니다. 스레드가 try 블록의 코드를 실행할 때 Abort가 호출되면 해당 catch 블록으로 점프합니다. . catch 블록 내에서 실행되면 catch 블록의 코드를 실행한 후 종료됩니다. (ResetAbort가 catch 블록 내에서 실행되는 경우에는 다릅니다. 현재 Abort 요청을 취소하고 아래쪽으로 계속 실행됩니다. 따라서 스레드가 종료되는지 확인하려면 위 예와 같이 Join 을 사용하는 것이 가장 좋습니다.
2. 스레드풀
ThreadPool(스레드 풀)은 여러 스레드가 필요한 짧은 작업(예: 자주 차단되는 스레드)에 적합합니다. 단점은 생성된 스레드를 제어할 수 없으며 우선 순위를 설정할 수 없다는 것입니다. 각 프로세스에는 단 하나의 스레드 풀만 있고 각 애플리케이션 도메인에는 단 하나의 스레드 풀(라인)만 있으므로 ThreadPool 클래스의 멤버 함수는 모두 정적임을 알 수 있습니다! ThreadPool.QueueUserWorkItem, ThreadPool.RegisterWaitForSingleObject 등을 처음 호출하면 스레드 풀 인스턴스가 생성됩니다. 다음은 스레드 풀의 두 가지 기능에 대한 소개입니다.
public static bool QueueUserWorkItem( //호출이 성공하면 true를 반환합니다.
WaitCallback callBack,//생성될 스레드가 호출하는 대리자
객체 상태 //대리자에게 전달된 매개변수
)//대리자가 매개 변수를 사용하지 않는다는 점을 제외하면 또 다른 오버로드된 함수는 스레드 풀에서 사용 가능한 스레드 수가 0이 아닌 경우 생성될 스레드를 대기열에 추가하는 것입니다. 스레드 풀이 스레드를 생성했습니다. 개수가 제한되어 있으면(기본값은 25) 이 스레드가 생성됩니다. 그렇지 않으면 스레드 풀에 대기하고 사용 가능한 스레드가 생길 때까지 기다립니다.
공개 정적 RegisteredWaitHandle RegisterWaitForSingleObject(
WaitHandle waitObject,//WaitHandle 등록
WaitOrTimerCallback callBack, // 스레드 호출 대리자
객체 상태,//대리자에게 전달된 매개변수
int TimeOut,//Timeout, 단위는 밀리초,
bool excuteOnlyOnce file:// 한 번만 실행할지 여부
);
공개 대리자 무효 WaitOrTimerCallback(
객체 상태, //즉, 대리자에게 전달된 매개변수
bool timedOut//true는 시간 초과 호출로 인한 것을 의미하고, 그렇지 않으면 waitObject를 의미합니다.
);
이 함수의 기능은 대기 스레드를 생성하는 것입니다. 이 함수가 호출되면 이 스레드는 waitObject 매개변수가 종료된 상태로 변경되거나 TimeOut이 설정된 시간에 도달할 때까지 "차단" 상태에 있게 됩니다. 이 "차단"은 스레드의 WaitSleepJoin 상태와 매우 다르다는 점에 주목할 가치가 있습니다. 스레드가 WaitSleepJoin 상태에 있으면 CPU는 스레드를 정기적으로 깨워 상태 정보를 폴링하고 업데이트한 다음 다시 WaitSleepJoin 상태로 들어갑니다. 스레드 전환은 매우 리소스 집약적이며 이 기능으로 생성된 스레드는 CPU 시간을 차지하지 않고 스레드 전환 시간을 낭비하지도 않습니다. 언제 실행해야 하는지 아시나요? 실제로 스레드 풀은 이러한 트리거 조건을 모니터링하기 위해 일부 보조 스레드를 생성하며, 조건이 충족되면 해당 스레드도 시작됩니다. 물론 이러한 보조 스레드 자체도 시간이 걸리지만 더 많은 대기 시간을 생성해야 하는 경우 스레드를 사용하려면 스레드 풀의 장점이 더욱 분명해집니다. 아래 예를 참조하세요.
static AutoResetEvent ev=new AutoResetEvent(false);
공개 정적 정수 Main(string[] args)
{ ThreadPool.RegisterWaitForSingleObject(
이브,
새로운 WaitOrTimerCallback(WaitThreadFunc),
4,
2000년,
false//로그아웃하고 대기할 때까지 대기 작업이 완료될 때마다 타이머가 재설정됨을 나타냅니다.
);
ThreadPool.QueueUserWorkItem(새로운 WaitCallback(ThreadFunc),8);
스레드.수면(10000);
0을 반환합니다.
}
공개 정적 무효 ThreadFunc(객체 b)
{ Console.WriteLine("개체는 {0}입니다.",b);
for(int i=0;i<2;i++)
{ 스레드.수면(1000);
ev.Set();
}
}
공개 정적 무효 WaitThreadFunc(객체 b,bool t)
{ Console.WriteLine("개체는 {0}이고,t는 {1}입니다.",b,t);
}
작업 결과는 다음과 같습니다.
개체는 8입니다.
객체는 4이고 t는 False입니다.
객체는 4이고 t는 False입니다.
개체는 4이고 t는 True입니다.
개체는 4이고 t는 True입니다.
개체는 4이고 t는 True입니다.
위 결과에서 ThreadFunc 스레드가 한 번 실행되고 WaitThreadFunc 스레드가 5번 실행되었음을 알 수 있습니다. WaitOrTimerCallback의 bool t 매개변수에서 이 스레드를 시작하는 이유를 판단할 수 있습니다. t가 false이면 waitObject를 의미하고, 그렇지 않으면 시간 초과로 인한 것입니다. 또한 객체 b를 통해 일부 매개변수를 스레드에 전달할 수도 있습니다.
3. 타이머
주기적으로 호출해야 하는 메소드에 적합하며, 타이머를 생성한 스레드에서 실행되지 않습니다. 시스템에서 자동으로 할당한 별도의 스레드에서 실행됩니다. 이는 Win32의 SetTimer 메서드와 비슷합니다. 그 구조는 다음과 같습니다:
공개 타이머(
TimerCallback 콜백,//호출할 메서드
객체 상태,//콜백에 전달된 매개변수
int DueTime,//콜백 호출을 시작하는 데 얼마나 걸립니까?
int period//이 메소드를 호출하는 시간 간격
); // DueTime이 0이면 콜백은 첫 번째 호출을 즉시 실행합니다. DueTime이 Infinite인 경우 콜백은 해당 메서드를 호출하지 않습니다. 타이머는 비활성화되어 있지만 Change 메서드를 사용하여 다시 활성화할 수 있습니다. period가 0 또는 Infinite이고 DueTime이 Infinite가 아닌 경우 콜백은 해당 메서드를 한 번 호출합니다. 타이머의 주기적 동작은 비활성화되지만 Change 메서드를 사용하여 다시 활성화할 수 있습니다. period가 0 또는 Infinite이고 DueTime이 Infinite가 아닌 경우 콜백은 해당 메서드를 한 번 호출합니다. 타이머의 주기적 동작은 비활성화되지만 Change 메서드를 사용하여 다시 활성화할 수 있습니다.
타이머를 생성한 후 타이머의 기간과 기한을 변경하려면 Timer의 Change 메서드를 호출하여 변경할 수 있습니다.
공개 bool 변경(
정수 기한 시간,
int 기간
);//분명히 변경된 두 매개변수는 타이머의 두 매개변수에 해당합니다.
공개 정적 정수 Main(string[] args)
{
Console.WriteLine("기간은 1000입니다");
타이머 tm=새 타이머(새 TimerCallback(TimerCall),3,1000,1000);
스레드.수면(2000);
Console.WriteLine("기간은 500입니다");
tm.Change (0,800);
스레드.수면(3000);
0을 반환합니다.
}
공개 정적 무효 TimerCall(객체 b)
{
Console.WriteLine("타이머콜백; b는 {0}입니다",b);
}
작업 결과는 다음과 같습니다.
기간은 1000입니다
타이머콜백;b는 3입니다.
타이머콜백;b는 3입니다.
기간은 500입니다
타이머콜백;b는 3입니다.
타이머콜백;b는 3입니다.
타이머콜백;b는 3입니다.
타이머콜백;b는 3입니다.
요약
위의 간략한 소개에서 각각 사용되는 경우를 볼 수 있습니다. Thread는 스레드의 복잡한 제어가 필요한 경우에 적합하고 ThreadPool은 여러 스레드가 필요한 짧은 작업(예: Thread가 자주 차단되는 경우)에 적합합니다. ) 타이머는 주기적으로 호출해야 하는 메서드에 적합합니다. 사용 특성을 이해하면 적절한 방법을 선택할 수 있습니다.