Windows平台C语言多线程

Windows平台C语言多线程

1、windows中的示例代码
// 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);
}

2、windows平台多线程API

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define NUM_THREADS 4
#define ARRAY_SIZE 1000

int array[ARRAY_SIZE];
int max_value = 0;
HANDLE max_value_mutex;  // 定义互斥句柄

DWORD WINAPI find_max(LPVOID lpParam) {
    long tid = (long)lpParam;
    int local_max = 0;

    int start = tid * (ARRAY_SIZE / NUM_THREADS);  // 每个线程处理的起始索引
    int end = (tid + 1) * (ARRAY_SIZE / NUM_THREADS); // 线程处理的最终索引

    for (int i = start; i < end; i++) {
        if (array[i] > local_max) {
            local_max = array[i];
        }
    }

    WaitForSingleObject(max_value_mutex, INFINITE);  // 获取到互斥锁的信号并无限等待,知道互斥锁信号为signaled,调用时会被阻塞,知道等待到互斥锁的信号
    if (local_max > max_value) {
        max_value = local_max;
    }
    ReleaseMutex(max_value_mutex);  // 释放全局变量,允许其他资源访问

    return 0;
}

int main() {
    HANDLE threads[NUM_THREADS];  // 定义线程数
    DWORD rc;
    long t;

    max_value_mutex = CreateMutex(NULL, FALSE, NULL);

    for (int i = 0; i < ARRAY_SIZE; i++) {
        array[i] = rand() % 1000;
    }

    for (t = 0; t < NUM_THREADS; t++) {
        threads[t] = CreateThread(NULL, 0, find_max, (LPVOID)t, 0, NULL);  // 创建线程,返回句柄操作该线程
        if (threads[t] == NULL) {
            printf("ERROR: Unable to create thread\n");
            ExitProcess(-1);
        }
    }

    WaitForMultipleObjects(NUM_THREADS, threads, TRUE, INFINITE);

    printf("The maximum value in the array is: %d\n", max_value);

    CloseHandle(max_value_mutex); // 关闭句柄

    ExitProcess(0);
}


/* 注释1 */

// DWORD 是 Windows API 中定义的一种数据类型,它表示双字(Double Word),即一个 32 位的无符号整数。WINAPI 是一个宏,用于指定函数调用约定,表示该函数使用的是标准的 Windows API 调用约定。

// DWORD WINAPI 是在 Windows 环境下声明函数的一种约定,它告诉编译器和链接器这个函数应该使用的是 Windows API 的调用约定,并且返回一个 DWORD 类型的值。

// WINAPI 宏会根据不同的编译环境做不同的处理。在 32 位编译环境下,它会被定义为空,而在 64 位编译环境下,它会被定义为 __stdcall。__stdcall 是一种标准的函数调用约定,它会将参数按照从右向左的顺序入栈,由被调用的函数负责清理栈空间。

// 综上所述,DWORD WINAPI 表示函数返回一个 32 位的无符号整数,并且使用 Windows API 的调用约定。这种约定适用于在 Windows 环境下编写使用 Windows API 的函数

/* 注释2 */

// LPVOID 是一个指向 void 类型的指针。在 Windows API 中经常使用 LP 前缀来表示长指针(Long Pointer),但在实际上它与普通的指针类型没有本质区别。

// lpParam 是一个函数的参数,它被声明为 LPVOID 类型,表示可以接受任意类型的指针。在 Windows API 中,通常使用 LPVOID 类型来传递任意类型的参数给线程函数。

// 在多线程编程中,你可能会希望在线程创建时传递一些额外的参数给线程函数,而这些参数的类型可以是任意的。通过将参数声明为 LPVOID 类型,你可以将任意类型的指针转换为 LPVOID 类型,然后在线程函数中将其转换回原来的类型,以获取传递给线程的实际参数。

// 例如,在 Windows 线程函数 CreateThread 中,你可以将 lpParam 参数用于传递给线程函数的参数,然后在线程函数内部进行类型转换,以获取原始的参数。

/* 注释3:start 和 end */

// 在多线程环境下,start 和 end 通常用于确定每个线程需要处理的数据范围。这种情况通常发生在将数据划分为多个部分,每个线程处理其中一部分的情况下。

// 在示例中,start 和 end 表示每个线程需要处理的数组索引范围。通过计算出每个线程负责处理的部分,可以有效地将任务分配给多个线程,从而提高程序的并发性能。

// 具体而言,start 表示当前线程需要处理的起始索引,而 end 表示当前线程需要处理的结束索引(不包含)。例如,如果 start 是 0,end 是 100,那么当前线程将处理数组的索引从 0 到 99 的元素。

// 通过这种方式,每个线程都可以并行处理数组的不同部分,从而提高整体处理速度。

/* 注释3:多线程中句柄 */

// 在 Windows 环境下,HANDLE 是一种句柄类型,它可以用来表示各种资源,包括文件、事件、互斥体、信号量等。在这里,max_value_mutex 是一个互斥体的句柄,用于实现多线程之间的互斥访问,确保对共享资源的安全访问。

// 互斥体(Mutex)是一种同步对象,用于实现多线程之间的互斥访问。它提供了两个主要的操作:锁定(lock)和解锁(unlock)。当一个线程获得了互斥体的锁,其他线程就不能再访问受保护的资源,直到锁被释放。

// 在示例代码中,max_value_mutex 被用来保护 max_value 全局变量的访问,以确保多个线程在更新 max_value 变量时不会发生竞争条件。每个线程在更新 max_value 之前,首先需要获取 max_value_mutex 的锁,在更新完成后释放锁。

// 通过使用互斥体,可以确保在任意时刻只有一个线程能够访问 max_value 变量,从而避免了多线程之间的竞争条件和数据不一致性问题。

/* 注释3:同步函数 */
// WaitForSingleObject 函数是 Windows API 中的一个同步函数,用于等待一个对象的状态发生变化。在这里,WaitForSingleObject 用于等待互斥体对象的信号状态。max_value_mutex 是一个互斥体的句柄,表示等待的对象。

// WaitForSingleObject 函数的第二个参数指定了等待时间,这里使用了 INFINITE 宏,表示无限等待,即直到互斥体对象的状态发生变化。

// 在多线程编程中,WaitForSingleObject 通常与互斥体一起使用,用于实现线程之间的同步。在这个例子中,当线程调用 WaitForSingleObject(max_value_mutex, INFINITE) 时,它会被阻塞,直到获取了 max_value_mutex 的锁,也就是直到互斥体对象的状态变为 signaled(有信号)状态。

一旦线程成功获取了锁,就可以安全地访问被保护的资源,例如更新全局变量 max_value。在这之后,线程应该调用 ReleaseMutex(max_value_mutex) 来释放互斥体对象的锁,以允许其他线程访问受保护的资源。

总的来说,WaitForSingleObject(max_value_mutex, INFINITE) 用于等待获取互斥体对象的锁,以保护共享资源的访问。

1、线程创建函数

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  SIZE_T                 dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID                 lpParameter,
  DWORD                  dwCreationFlags,
  LPDWORD                lpThreadId
);

/*
   * lpThreadAttributes:指向 SECURITY_ATTRIBUTES 结构的指针,用于指定新线程的安全性属性。一般情况下设为     NULL。
   * dwStackSize:新线程的堆栈大小,一般设为 0 使用默认大小。
   * lpStartAddress:指向线程函数的指针,即新线程的入口点。
   * lpParameter:传递给线程函数的参数,可以是任意类型的指针。
   * dwCreationFlags:线程创建的标志,一般设为 0。
   * lpThreadId:指向一个 DWORD 变量的指针,用于接收新线程的标识符。
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值