マルチスレッドの C サンプル プログラム
Bounce.c は、a
または A
がキーボードから入力されるたびに新しいスレッドを作成する、マルチスレッドのサンプル プログラムです。 各スレッドは、それぞれ異なる色の文字を画面上で移動します。 最大で 32 のスレッドを作成できます。 q
または Q
がキーボードから入力されると、プログラムは正常に終了します。
マルチスレッド プログラムをコンパイルしてリンクする
プログラムは、既定ではマルチスレッドとしてコンパイルされます。
開発環境内からマルチスレッド プログラム Bounce.c をコンパイルしてリンクするには
[ファイル] メニューで、 [新規]>[プロジェクト]をクリックします。
[新しいプロジェクトの作成] ダイアログで、[C++] タグ、[Windows] タグ、[コンソール] タグを含む [コンソール アプリ] テンプレートを選択します。 [次へ] を選択して続行します。
[新しいプロジェクトの構成] ダイアログ で、ご自身のプロジェクトの名前 ("Bounce" など) を入力します。 [作成] を選択して続行します。
ソリューション エクスプローラー ウィンドウで、ご自身のプロジェクトの [ソース ファイル] フォルダーを開き、ご自身のソース ファイルの名前を .c 拡張子に変更します。
編集ウィンドウで、既存のソース コードを削除し、サンプル コードに置き換えます。
[ビルド] メニューの [ソリューションのビルド]をクリックします。
F5 キーを押して、デバッガーでプログラムを起動します。
[ファイル] メニューで、 [新規]>[プロジェクト]をクリックします。
[新しいプロジェクト] ダイアログでは、左ウィンドウで [Visual C++] を選択し、中央ウィンドウで [空のプロジェクト] を選択します。
[名前] 編集ボックスに、ご自身のプロジェクトの名前 ("Bounce" など) を入力します。 [OK] を選択して、空のプロジェクトを作成します。
ソリューション エクスプローラー ウィンドウで、ご自身のプロジェクトの [ソース ファイル] フォルダーを開き、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);
}