쓰레드를 쉬게 하는 방법은 Thread.Suspend를 이용할 수도 있다. Sleep과의 차이는 Sleep인 경우에는 지정한 시간 만큼 쉰다는 의미지만 Suspend인 경우에는 Resume 메소드를 호출할 때까지 쉬게 된다는 것이다. 또 다른 차이점은 Sleep 메소드는 현재 쓰레드를 일시중지하게 하는 정적 메소드로 자기 자신의 쓰레드만 쉬게 할 수 있다. 반면에 Suspend 는 자기 뿐 아니라 다른 쓰레드도 쉬게 할 수 있다. Suspend로 잠을 자고 있는 Thread가 스스로 깰 수는 없다. 그래서 Suspend로 쉬고 있는 쓰레드는 다른 쓰레드가 Resume을 이용하여 깨울 때 까지 쉬고 있는 것이다. 주의할 점은 Sleep인 경우는 쓰레드가 즉시 중단 되지만 Suspend로 쉬게하면 중단해도 되는 안전한 상황까지 즉시 중단 되지 않을 수도 있다.
Suspend 메소드로 쓰레드를 잠시 중지하면 Resume 메소드로 다시 동작하게 할 수 있지만 Thread.Abort로 일단 쓰레드를 종료시키면 다시 되살릴 수 없다.
using System;
using System.Threading;
public class Test
{
public bool sleep = false;
public void First()
{
for (int i = 0; i < 10; i++)
{
//1.5초 동안 쉬면서 F0, F1, ..., F5까지 출력하고 쉼
Thread.Sleep(1500);
Console.WriteLine("F{0}", i);
if (i == 5)
{
sleep = true;
Console.WriteLine("");
Console.WriteLine("first 쉼...");
Thread.CurrentThread.Suspend();
}
}
}
public static void Main()
{
Test t = new Test();
Thread first = new Thread(new ThreadStart(t.First));
first.Start();
//메인이 아래에서 대기 합니다.
//쓰레드가 실행하는 First 메서드에서 i가 5가 되면 쉬면서 sleep을 true로 만드는데, 그때까지 쉼, 6초 동안 쉼
스레드는 하나의 프로세스(실행 중인 프로그램) 내에 존재하는 순차적인 제어의 흐름을 두기 위해 사용한다. 즉 프로세스는 하나 이상의 스레드로 이루어 진다.
각 스레드는 코드, 데이터, 힙 영역은 공유하며, Stack Frame은 별도로 할당되는데 이를 Thread Stack이라하며 Thread Stack은 메소드 단위로 분리되어 할당되며 실행이 끝나면 Stack Frame은 사라진다.
멀티 스레드가 제대로 동작하기 위해서는 CPU가 여러 개 있어야 한다. 단일 CPU를 사용하게 되면 CPU는 한 번에 하나의 스레드를 사용한다. 만약 멀티 스레드로 프로그램이 실행되는 경우라면 CPU의 사용 시간을 나누어서 각각의 스레드에게 주기 때문에 단일 쓰레드와 별차이가 없다.
C#에서 스레드를 만들기 위해서는 기 정의된 Thread 클래스와 ThreadStart, ParameterizedThreadStart 델리게이트를 이용하면 된다.
public delegate void ParameterizedThreadStart(object obj);
한 개의 파라미터를 object 형식으로 전달하기 때문에 여러 개의 파라미터를 전달하기 위해서는 클래스나 구조체를 만들거나 배열 등을 이용하여 전달할 수 있다. 파라미터 전달은 Thread.Start() 메소드를 호출할 때 전달한다.
public delegate void ThreadStart();
스레드가 실행시킬 메소드가 파라미터를 받아들이지 않는 형태이며, ThreadStart를 이용해 파라미터를 전달하는 방법은 ThreadStart 델리게이트가 처다보는 메소드는 파라미터를 받아들이지 않으므로 그 메소드 안에서 다른 메소드를 호출하면서 파라미터를 전달할 수 있다.
C#에서 스레드를 위한 클래스들은 System.Threading 네임스페이스 안에 정의되어 있다.
[실습]
1부터 50까지의 합을 5개의 쓰레드에 나누어서 실행하고자 한다.
첫번째 쓰레드는 1~10 까지의 합을, 두번째 쓰레드는 11~20 까지의 합을.... 다섯번째 쓰레드는
41~50 사이의 합을 구하는데 아래 두 방법으로 프로그램을 작성하세요.
- ParameterizedThreadStart 델리게이트를 이용하여 작성하세요.
- ThreadStart 델리게이트를 이용하여 작성하세요
1. ParameterizedThreadStart 델리게이트를 이용하여 작성
using System;
using System.Threading;
namespace ConsoleApplication2
{
class Program
{
static int mysum = 0;
static void DoSomething(object n)
{
int sum = 0;
int[] number = (int[])n;
for (int i = number[0]; i <= number[1]; i++)
{
sum += i;
}
mysum += sum;
}
static void Main(string[] args)
{
Thread t1 = new Thread(new ParameterizedThreadStart(DoSomething));
Thread t2 = new Thread(new ParameterizedThreadStart(DoSomething));
Thread t3 = new Thread(new ParameterizedThreadStart(DoSomething));
Thread t4 = new Thread(new ParameterizedThreadStart(DoSomething));
Thread t5 = new Thread(new ParameterizedThreadStart(DoSomething));
C# 윈도우 프로그램은 이벤트(Event, 사건)에 반응해서 코드가 실행되는 이벤트 기반으로 만들어지며 이러한 이벤트는 사용자가 일으키는것이 아니라 윈도우 운영체제가 일으키는 것이다.
사용자가 키보드나 마우스를 제어하면 인터럽트가 발생되고 이를 운영체제가 받아 들이는 것이다. 운영체제는 이 인터럽트를 바탕으로 윈도우 메시지를 만든 뒤 이벤트를 받아야 하는 응용프로그램에 보내는 것이다.
윈도우 메시지는 종류가 다양하다. 윈도우 응용프로그램은 마우스 이동이나 클릭, 키보드 입력처럼 미리 등록되어 있는 메시지를 받지만 다른 응용프로그램은 자체적으로 정의한 메시지도 받을 수 있다.
Application 클래스는 응용프로그램이 받고 있는 수많은 메시지 중 관심 있는 메시지만 걸러낼 수 있는 메시지 필터링 기능이 있다. 예를 들어 윈도우 응용프로그램에서 ALT+F4를 눌러 윈도우가 종료되는 것을 막고 싶다면 필터링 기능을 이용하여 이를 차단하는 것도 가능하다.
일반적으로 윈도우 메시지는 식별번호가 붙어 있는데 WM_LBUTTONDOWN 메시지 ID가 0x201, WM_LBUTTONUP 메시지 ID가 0x202로 정의되어 있다.
public const int WM_KEYDOWN = 0x100; public const int WM_KEYUP = 0x101; public const int WM_COMMAND = 0x111; public const int WM_LBUTTONDOWN = 0x201; public const int WM_LBUTTONUP = 0x202; public const int WM_LBUTTONDBLCLK = 0x203; public const int WM_RBUTTONDOWN = 0x204; public const int WM_RBUTTONUP = 0x205; public const int WM_RBUTTONDBLCLK = 0x206;
Application 클래스는 특정 ID를 갖는 메시지를 필터에 등록해 두었다가 응용프로그램에 메시지가 전달되면 해당 필터를 동작시키고, 메시지가 필터링 되지 않았다면 해당 메시지를 폼이나 컨트롤로 보내 이벤트를 발생시킨다.
Application 클래스의 AddMessageFilter() 메소드는 응용프로그램에 메시지 필터를 등록한다. 이 메소드의 매개변수로 IMessageFilter를 구현한 클래스의 인스턴스를 매개변수로 받으며 IMessageFilter의 PreFilterMessage를 구현해야 한다.
public class MessageFilter : IMessageFilter {
public bool PreFilterMessage(ref Message m) { //m.Msg는 메시지 ID
//마우스 왼쪽, 오른쪽, 가운데 버튼동작, 마우스 휠굴림 메시지
//
if (m.Msg >= 0x200 && m.Msg <= 0x20E) {
Console.WriteLine(“메시지 : “ + m.Msg);
return true; //메시지처리했으니 더 관심가질 필요없다.
}
return false; //메시지를 건드리지 않았으니 응용프로그램에서 처리해라
}
}
위와 같이 메시지 필터를 구현했다면 AddMessageFilter() 메소드를 호출해서 등록하면 된다.
Application.AddMessageFilter( new MessageFilter());