C++-线程创建

在C++中,线程创建可以通过Windows API 提供的函数CreateThread和CRT 提供的函数_beginthreadex,也可以使用标准库中的thread。

CreateThread

包含在#include<windows.h>中

函数原型:

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

参数的意义分别是:

  • lpThreadAttributes:用于指定线程对象的安全描述符(一般传入 NULL)。
  • dwStackSize:用于指定线程堆栈的大小,如果传入 0,则默认使用父进程的堆栈大小。
  • lpStartAddress:指向线程函数的指针,即新线程启动时要执行的函数。
  • lpParameter:传递给线程函数的参数。
  • dwCreationFlags:用于指定线程的创建标志,例如设置为 CREATE_SUSPENDED 可以让线程创建后暂停执行。
  • lpThreadId:用于接收新线程的线程标识符,是一个输出参数。

返回值是HANDLE类型,通过这个句柄可以对线程进行操作。

#include <iostream>
#include <Windows.h>
using namespace std;
DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    std::cout << "Hello from the new thread!" << std::endl;
    return 0;
}

int main() {
    HANDLE hThread;
    DWORD dwThreadId;
    cout << "This is main thread " <<endl;
    hThread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, &dwThreadId);
    cout << "new thread id == "<< dwThreadId << endl;
    if (hThread == NULL) {
        std::cerr << "Failed to create thread!" << std::endl;
        return 1;
    }

    // 等待线程结束
    WaitForSingleObject(hThread, INFINITE);

    CloseHandle(hThread);

    std::cout << "Thread execution completed." << std::endl;

    return 0;
}

在上面那段代码中,主线程使用createThread函数创建了一个子线程,然后主线程阻塞等待子线程执行,然后才结束。

当调用WaitForSingleObject()的时候,程序会阻塞在这里,直到被等待的对象状态变为 signaled。在等待过程中,当前线程会被挂起,直到等待超时或被等待的对象进入 signaled 状态。(当一个线程正在运行时,管理它内核对象处于non-signaled状态;而当线程结束运行时,管理它的内核对象将处于signaled状态。)

函数原型:

DWORD WaitForSingleObject(
  HANDLE hHandle,
  DWORD  dwMilliseconds
);
  • hHandle:要等待的对象的句柄。
  • dwMilliseconds:等待的时间,以毫秒为单位。如果设置为 INFINITE,表示无限等待。

WaitForMultipleObjects的功能和WaitForSingleObject是相似的,只不过他可以同时等待多个线程。

DWORD WaitForMultipleObjects(
  DWORD        nCount,
  const HANDLE *lpHandles,
  BOOL         bWaitAll,
  DWORD        dwMilliseconds
);
  • nCount:等待的对象句柄数量。
  • lpHandles:指向包含对象句柄数组的指针。
  • bWaitAll:指定是等待所有对象进入 signaled 状态(TRUE),还是只要有一个对象进入 signaled 状态就返回(FALSE)。
  • dwMilliseconds:等待的时间,以毫秒为单位。如果设置为 INFINITE,表示无限等待。

WaitForMultipleObjects 函数会等待指定的内核对象数组中的一个或全部对象进入 signaled 状态。如果 bWaitAll 参数为 TRUE,则只有当数组中所有对象都进入 signaled 状态时,函数才会返回;如果为 FALSE,则只要有一个对象进入 signaled 状态,函数就会立即返回。

#include <iostream>
#include <Windows.h>

DWORD WINAPI ThreadFunc1(LPVOID lpParam) {
    std::cout << "Thread 1 executing..." << std::endl;
    return 0;
}

DWORD WINAPI ThreadFunc2(LPVOID lpParam) {
    std::cout << "Thread 2 executing..." << std::endl;
    return 0;
}

int main() {
    HANDLE hThreads[2];
    DWORD dwThreadId1, dwThreadId2;

    hThreads[0] = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, &dwThreadId1);
    hThreads[1] = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, &dwThreadId2);

    if (hThreads[0] == NULL || hThreads[1] == NULL) {
        std::cerr << "Failed to create threads!" << std::endl;
        return 1;
    }

    // 等待两个线程的执行结束
    WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);

    CloseHandle(hThreads[0]);
    CloseHandle(hThreads[1]);

    std::cout << "Both threads have completed execution." << std::endl;

    return 0;
}

与createThread对应的函数是Closethread函数,他的作用是关闭利用当前线程。

_beginthreadex

除了createThread以外,还有_beginthreadex,#include<process.h>头文件中。

函数原型:

uintptr_t _beginthreadex(
    void* security,
    unsigned stack_size,
    unsigned (__stdcall* start_address )(void*),
    void* arglist,
    unsigned initflag,
    unsigned* thrdaddr
);

参数的意义:

  • security:指向安全描述符的指针,通常可以设置为 NULL。
  • stack_size:指定线程堆栈的大小,通常可以设置为 0 表示使用默认堆栈大小。
  • start_address:线程函数的入口点地址,必须是无返回值的函数,并接受一个 void* 类型的参数。
  • arglist:传递给线程函数的参数。
  • initflag:标志位,通常可以设置为 0。
  • thrdaddr:指向用于存储新线程标识符的变量的指针。
#include <iostream>
#include <process.h>

unsigned __stdcall ThreadFunc(void* data) {
    int* ptr = reinterpret_cast<int*>(data);
    std::cout << "Thread ID: " << GetCurrentThreadId() << ", Received data: " << *ptr << std::endl;
    return 0;
}

int main() {
    int data = 42;
    uintptr_t threadHandle;
    
    // 创建新线程
    threadHandle = _beginthreadex(NULL, 0, ThreadFunc, &data, 0, NULL);
    
    if (threadHandle == -1) {
        std::cerr << "Failed to create thread." << std::endl;
        return 1;
    }

    // 等待线程结束
    WaitForSingleObject(reinterpret_cast<HANDLE>(threadHandle), INFINITE);

    std::cout << "Main thread exiting." << std::endl;

    return 0;
}

这两个函数的一些区别:

  1. 返回值类型

    • CreateThread 返回一个 HANDLE 类型的线程句柄,用于标识新创建的线程。
    • _beginthreadex 返回一个 uintptr_t 类型的线程标识符,用于标识新创建的线程。
  2. 使用方式

    • CreateThread 是 Windows API 中提供的函数,在使用时需要包含 <Windows.h> 头文件。
    • _beginthreadex 是 CRT(C Runtime Library)中提供的函数,在使用时需要包含 <process.h> 头文件。
  3. 参数类型

    • CreateThread 的线程函数入口点是 LPTHREAD_START_ROUTINE 类型,需要是 DWORD WINAPI 形式的线程函数。
    • _beginthreadex 的线程函数入口点是 unsigned (__stdcall* start_address)(void*) 类型,也需要是 unsigned __stdcall 形式的线程函数。
  4. 堆栈

    • 在使用 CreateThread 创建线程时,需要自行管理线程堆栈的大小。
    • 使用 _beginthreadex 创建线程时,可以指定线程堆栈的大小,如果为 0 则使用默认值。
  5. 运行时库

    • CreateThread 是直接调用 Windows API,不依赖于 CRT。
    • _beginthreadex 是 CRT 中提供的函数,依赖于 CRT 运行时库。 
  6. 回收:
    • 使用CreateThread创建线程,回收线程就会自动调用ExitThread,在ExitThread回收线程的时候,不会回收C++运行时库(如strcpy()就属于C++运行时库)申请的空间,就会造成内存泄漏
    • 要使用_beginthreadex创建线程,回收线程就会自动调用_endthreadex,在这个函数里会先回收申请的空间,然后再调用ExitThread;

最主要的还是关于回收的情况,所以使用_beginthreadex一般会更安全一点。

thread

在C++的标准库#include<thread>中,可以直接定义出一个thread类型的线程。

#include <iostream>
#include <thread>
using namespace std;
// 线程函数
void threadFunction() {
    cout << "Hello from thread!" <<endl;
}

int main() {
    // 创建一个新线程,将线程函数作为参数传递
    thread t(threadFunction);

    cout << "Hello from main!" <<endl;

    // 等待子线程执行完毕
    t.join();

    return 0;
}

除了基本的线程创建和管理,std::thread 还提供了其他功能,如获取和设置线程标识、检查线程是否可执行、与其他线程交互等。此外,C++11 还引入了一系列线程相关的工具,如互斥量、条件变量等,用于实现线程同步和通信。就不详细展开了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值