샘플 다중 스레드 C 프로그램
Bounce.c는 문자를 a
입력할 A
때마다 새 스레드를 만드는 샘플 다중 스레드 프로그램입니다. 각 스레드는 화면 주위에 다른 색의 문자를 반송합니다. 최대 32개의 스레드를 만들 수 있습니다. 프로그램의 정상적인 종료는 입력되거나 Q
입력될 때 q
발생합니다.
다중 스레드 프로그램 컴파일 및 연결
프로그램은 기본적으로 다중 스레드로 컴파일됩니다.
개발 환경 내에서 다중 스레드 프로그램 Bounce.c를 컴파일하고 연결하려면
파일 메뉴에서 새로 만들기>프로젝트를 차례로 선택합니다.
새 프로젝트 만들기 대화 상자에서 C++, Windows 및 콘솔 태그가 있는 콘솔 앱 템플릿을 선택합니다. 다음 을 선택하여 계속 진행합니다.
새 프로젝트 구성 대화 상자에서 프로젝트의 이름(예: "Bounce")을 입력합니다. 만들기를 선택하여 계속합니다.
솔루션 탐색기 창에서 프로젝트 아래에 있는 원본 파일 폴더를 열고 원본 파일의 이름을 확장명이 .c로 변경합니다.
편집 창에서 기존 소스 코드를 삭제하고 샘플 코드로 바꿉다.
빌드 메뉴에서 솔루션 빌드를 선택합니다.
F5 키를 눌러 디버거에서 프로그램을 시작합니다.
파일 메뉴에서 새로 만들기>프로젝트를 차례로 선택합니다.
새 프로젝트 대화 상자의 왼쪽 창에서 Visual C++를 선택한 다음 가운데 창에서 빈 프로젝트를 선택합니다.
이름 편집 상자에 프로젝트의 이름(예: "Bounce")을 입력합니다. 확인을 선택하여 빈 프로젝트를 만듭니다.
솔루션 탐색기 창에서 프로젝트 아래에 있는 원본 파일 폴더를 열고 C 소스 코드가 포함된 파일을 프로젝트에 추가합니다.
빌드 메뉴에서 솔루션 빌드 명령을 선택하여 프로젝트를 빌드합니다.
F5 키를 눌러 디버거에서 프로그램을 시작합니다.
새 스레드를 만들려면 a 키를 누릅니다. 각 스레드는 화면 주위에 다른 색의 문자를 반송합니다.
종료하려면 q 키를 누릅니다.
명령줄에서 다중 스레드 프로그램 Bounce.c를 컴파일하고 연결하려면
Visual Studio 도구 명령 프롬프트를 엽니다. 이렇게 하면 경로가 컴파일러를 포함하도록 설정됩니다.
프로그램을 컴파일하고 연결합니다.
cl bounce.c
예시
명령줄에서 빌드하려면 .c 확장명을 사용하여 이 샘플을 복사하여 원본 파일에 저장합니다. IDE에서 템플릿에서 만든 소스 코드를 다음 샘플로 바꿉다.
// sample_multithread_c_program.c
// compile with: /c
//
// Bounce - Creates a new thread each time the letter 'a' is typed.
// Each thread bounces a character of a different color around
// the screen. All threads are terminated when the letter 'Q' is
// entered.
//
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <conio.h>
#include <process.h>
#define MAX_THREADS 32
// The function getrandom returns a random number between
// min and max, which must be in integer range.
#define getrandom( min, max ) (SHORT)((rand() % (int)(((max) + 1) - \
(min))) + (min))
int main(void); // Thread 1: main
void KbdFunc(void); // Keyboard input, thread dispatch
void BounceProc(void* pMyID); // Threads 2 to n: display
void ClearScreen(void); // Screen clear
void ShutDown(void); // Program shutdown
void WriteTitle(int ThreadNum); // Display title bar information
HANDLE hConsoleOut; // Handle to the console
HANDLE hRunMutex; // "Keep Running" mutex
HANDLE hScreenMutex; // "Screen update" mutex
int ThreadNr = 0; // Number of threads started
CONSOLE_SCREEN_BUFFER_INFO csbiInfo; // Console information
COORD consoleSize;
BOOL bTrails = FALSE;
HANDLE hThreads[MAX_THREADS] = { NULL }; // Handles for created threads
int main(void) // Thread One
{
// Get display screen information & clear the screen.
hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsoleOut, &csbiInfo);
consoleSize.X = csbiInfo.srWindow.Right;
consoleSize.Y = csbiInfo.srWindow.Bottom;
ClearScreen();
WriteTitle(0);
// Create the mutexes and reset thread count.
hScreenMutex = CreateMutexW(NULL, FALSE, NULL); // Cleared
hRunMutex = CreateMutexW(NULL, TRUE, NULL); // Set
// Start waiting for keyboard input to dispatch threads or exit.
KbdFunc();
// All threads done. Clean up handles.
if (hScreenMutex) CloseHandle(hScreenMutex);
if (hRunMutex) CloseHandle(hRunMutex);
if (hConsoleOut) CloseHandle(hConsoleOut);
}
void ShutDown(void) // Shut down threads
{
// Tell all threads to die
ReleaseMutex(hRunMutex);
while (ThreadNr > 0)
{
// Wait for each thread to complete
WaitForSingleObject(hThreads[--ThreadNr], INFINITE);
}
// Clean up display when done
WaitForSingleObject(hScreenMutex, INFINITE);
ClearScreen();
}
void KbdFunc(void) // Dispatch and count threads.
{
int KeyInfo;
do
{
KeyInfo = _getch();
if (tolower(KeyInfo) == 'a' &&
ThreadNr < MAX_THREADS)
{
++ThreadNr;
hThreads[ThreadNr] =
(HANDLE)_beginthread(BounceProc, 0, (void*)(uintptr_t)ThreadNr);
WriteTitle(ThreadNr);
}
if (tolower(KeyInfo) == 't')
{
bTrails = !bTrails;
}
} while (tolower(KeyInfo) != 'q');
ShutDown();
}
void BounceProc(void* pMyID)
{
wchar_t MyCell, OldCell;
WORD MyAttrib, OldAttrib = 0;
wchar_t BlankCell = 0x20;
COORD Coords, Delta;
COORD Old = { 0,0 };
DWORD Dummy;
int MyID = (int)(uintptr_t)pMyID;
// Generate update increments and initial
// display coordinates.
srand(MyID * 3);
Coords.X = getrandom(0, consoleSize.X - 1);
Coords.Y = getrandom(0, consoleSize.Y - 1);
Delta.X = getrandom(-3, 3);
Delta.Y = getrandom(-3, 3);
// Set up character & generate color
// attribute from thread number.
if (MyID > 16)
MyCell = (wchar_t)(0x60 + MyID - 16); // lower case
else
MyCell = (wchar_t)(0x40 + MyID); // upper case
MyAttrib = MyID & 0x0f; // force black background
do
{
// Wait for display to be available, then lock it.
WaitForSingleObject(hScreenMutex, INFINITE);
if (!bTrails)
{
// If we still occupy the old screen position, blank it out.
ReadConsoleOutputCharacterW(hConsoleOut, &OldCell, 1,
Old, &Dummy);
ReadConsoleOutputAttribute(hConsoleOut, &OldAttrib, 1,
Old, &Dummy);
if ((OldCell == MyCell) && (OldAttrib == MyAttrib))
WriteConsoleOutputCharacterW(hConsoleOut, &BlankCell, 1,
Old, &Dummy);
}
// Draw new character, then clear screen lock
WriteConsoleOutputCharacterW(hConsoleOut, &MyCell, 1,
Coords, &Dummy);
WriteConsoleOutputAttribute(hConsoleOut, &MyAttrib, 1,
Coords, &Dummy);
ReleaseMutex(hScreenMutex);
// Increment the coordinates for next placement of the block.
Old.X = Coords.X;
Old.Y = Coords.Y;
Coords.X += Delta.X;
Coords.Y += Delta.Y;
// If we are about to go off the screen, reverse direction
if (Coords.X < 0 || Coords.X >= consoleSize.X)
{
Delta.X = -Delta.X;
Beep(400, 50);
}
if (Coords.Y < 0 || Coords.Y > consoleSize.Y)
{
Delta.Y = -Delta.Y;
Beep(600, 50);
}
}
// Repeat while RunMutex is still taken.
while (WaitForSingleObject(hRunMutex, 75L) == WAIT_TIMEOUT);
}
void WriteTitle(int ThreadNum)
{
enum
{
sizeOfNThreadMsg = 120
};
wchar_t NThreadMsg[sizeOfNThreadMsg] = { L"" };
swprintf_s(NThreadMsg, sizeOfNThreadMsg,
L"Threads running: %02d. Press 'A' "
L"to start a thread, 'T' to toggle "
L"trails, 'Q' to quit.", ThreadNum);
SetConsoleTitleW(NThreadMsg);
}
void ClearScreen(void)
{
DWORD dummy = 0;
COORD Home = { 0, 0 };
FillConsoleOutputCharacterW(hConsoleOut, L' ',
consoleSize.X * consoleSize.Y,
Home, &dummy);
}