C++的多线程应用

一、多线程

在学习多线程之前,我们先了解一下什么是任务。

任务顾名思义,任务就是需要完成的活动,或者说是实现某个目的的一系列操作。

举个简单的例子:在我们日常生活中吃饭就是一项任务,玩手机也是一项任务。

那什么是多任务呢?多任务就是多个简单任务。同样举个简单的例子,我们边吃饭边玩手机。那吃饭和玩手机两个简单任务合在一起,我们就称之为多任务。

在了解完任务以后,我们再来了解一下什么是线程。

线程:在操作系统中,线程是进行程序执行和资源分配的最小单位,它是进程的一个实体,是CPU调度和分派的基本单位,也是比进程更小的能独立运行的基本单位。通俗一点的理解也可以说是执行一个任务的过程。

多线程:则是指在单个进程内部可以同时执行多个线程的能力,也就是我们常说的并发执行

我们同样举个简单的例子,比如在某一时刻,我们去执行吃饭的任务。那么在这一时刻,我们去吃饭的整个活动过程我们都可以把它当做线程。那我们边吃饭边玩手机就是多线程的执行。

注意,这里我们还提到了一个进程的概念,进程就是我们执行程序的一次执行过程。进程与程序还不一样,程序是指令和数据的有序集合,它本身是没有任何运行的含义,是一个静态的概念。而进程则是一个动态的概念。进程还是系统资源分配的单位。

了解到这里,我们就可以明白,进程就是程序的一次执行,所以进程里可以包含多个线程,而每个线程执行一个任务。当然,进程里至少要有一个线程,否则进程的存在就显得毫无意义。

在这里,我们还需要提及一个问题,那就是真假线程的问题。所谓假线程其实就是模拟线程。真正的多线程是指有多个CPU,即多核。像服务器等。而我们模拟出来的多线程,是在一个CPU的情况下,在同一时间点,CPU只能执行一个线程代码,但是CPU在不同的线程之间来回切换执行,切换速度达到微秒级,所以在我们看来就是并发执行的。比如我们边吃饭边玩手机,看似两个任务是同时进行,但事实上,在某一时刻我们只是在执行吃饭任务或玩手机任务,只是我们的大脑在两个任务之间来回执行的速度过快,导致我们以为我们是在同时执行。然而,在大多数编程语言中,如Java、Python、C++和C#等,我们可以在程序中创建线程,这些线程是由操作系统进行管理和调度的。这些线程是真正的操作系统线程,而不是模拟线程(想深入了解的可以学习操作系统相关知识),所以,通过使用多线程编程技术,我们可以充分利用多核处理器和多处理器的计算能力,提高程序的执行效率和响应能力。

二、C++的多线程实现

在使用C++创建多线程程序时,我们可以引用Windows API函数来创建和管理线程。所以我们需要先引用windows.h头文件。

#include <windows.h>  

然后,我们可以声明两个句柄变量和两个DWORD类型变量,用于存储线程的标识符和ID(这里我们以C++创建两个线程为例,所以只声明两个线程的标识符和ID)。

    HANDLE threadHandle1, threadHandle2;
    DWORD threadId1, threadId2;

紧接着,我们可以使用CreateThread函数在Windows操作系统中创建一个新的线程,并在新线程上运行,以下是CreateThread函数在Windows API中的声明:

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

接下来是对CreateThread函数中的参数的说明:

参数含义
lpThreadAttributes指向一个SECURITY_ATTRIBUTES结构的指针,该结构用于确定返回的线程句柄是否可以被子进程继承。如果此参数为NULL,则线程句柄不能被继承。
dwStackSize指定线程的初始堆栈大小。如果此参数为0,则使用与调用线程相同的堆栈大小。
 lpStartAddress指向线程函数的指针。当线程启动时,它从这里开始执行。
lpParameter指向传递给线程函数的参数的指针。
dwCreationFlags指定线程的启动和运行方式。
lpThreadId如果此参数不为NULL,则返回新线程的线程ID。

对CreateThread函数了解完以后,我们可以通过以下程序创建两个线程运行,并对线程是否创建成功做出判断:

    // 创建第一个线程  
    threadHandle1 = CreateThread(NULL, 0, ThreadProc1, NULL, 0, &threadId1);
    if (threadHandle1 == NULL) {
        std::cout << "Failed to create thread 1." << std::endl;
        return 1;
    }

    // 创建第二个线程  
    threadHandle2 = CreateThread(NULL, 0, ThreadProc2, NULL, 0, &threadId2);
    if (threadHandle2 == NULL) {
        std::cout << "Failed to create thread 2." << std::endl;
        CloseHandle(threadHandle1); // 关闭第一个线程的句柄  
        return 1;
    }

两个线程创建以后,我们还要创建相应的线程执行函数,如下:

DWORD WINAPI ThreadProc1(LPVOID lpParameter) 
{
    // 线程一执行的代码  
    std::cout << "Thread1 is running." << std::endl;
    return 0;
}

DWORD WINAPI ThreadProc2(LPVOID lpParameter) 
{
    // 线程二执行的代码  
    std::cout << "Thread2 is running." << std::endl;
    return 0;
}

其中DWORD WINAPI是线程函数的原型。ThreadProc1和ThreadProc2是线程函数名。LPVOID lpParameter是线程函数的参数。

在线程和线程函数创建运行以后,我们可以通过WaitForSingleObject函数来实现线程的执行等待,以下是WaitForSingleObject函数的声明:

DWORD WaitForSingleObject
(
    HANDLE  hHandle,
    DWORD   dwMilliseconds
);

参数说明:

参数含义
hHandle指定要等待的对象的句柄。这可以是线程的句柄、事件、互斥量等。
dwMilliseconds指定等待的时间,以毫秒为单位。如果此参数为INFINITE,则函数会无限期地等待,直到对象变为信号状态。

在对 WaitForSingleObject了解完以后,我们可以使用该函数同步并等待线程运行结束。

    // 等待两个线程执行完成  
    WaitForSingleObject(threadHandle1, INFINITE);
    WaitForSingleObject(threadHandle2, INFINITE);

最后当一个线程执行完以后,我们要使用 CloseHandle函数关闭已打开的线程句柄,然后进行资源释放。注意,如果不使用CloseHandle函数释放资源,在做大型项目开发时,可能会导致应用程序出现性能问题和稳定性问题。

以下是 CloseHandle函数的声明,我们只需要在括号里写上句柄变量名即可。

BOOL CloseHandle(  
  HANDLE  hObject  
);

下面是完整的C++创建线程程序:

#include <iostream>  
#include <windows.h>  

DWORD WINAPI ThreadProc1(LPVOID lpParameter) {
    // 线程执行的代码  
    std::cout << "Thread1 is running." << std::endl;
    return 0;
}

DWORD WINAPI ThreadProc2(LPVOID lpParameter) {
    // 线程执行的代码  
    std::cout << "Thread2 is running." << std::endl;
    return 0;
}


int main() {
    HANDLE threadHandle1, threadHandle2;
    DWORD threadId1, threadId2;

    // 创建第一个线程  
    threadHandle1 = CreateThread(NULL, 0, ThreadProc1, NULL, 0, &threadId1);
    if (threadHandle1 == NULL) {
        std::cout << "Failed to create thread 1." << std::endl;
        return 1;
    }

    // 创建第二个线程  
    threadHandle2 = CreateThread(NULL, 0, ThreadProc2, NULL, 0, &threadId2);
    if (threadHandle2 == NULL) {
        std::cout << "Failed to create thread 2." << std::endl;
        CloseHandle(threadHandle1); // 关闭第一个线程的句柄  
        return 1;
    }

    // 等待两个线程执行完成  
    WaitForSingleObject(threadHandle1, INFINITE);
    WaitForSingleObject(threadHandle2, INFINITE);

    // 关闭线程句柄  
    CloseHandle(threadHandle1);
    CloseHandle(threadHandle2);

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

    return 0;
}

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值