首页 > 系统相关 >【C++】多线程(基于Windows以及pthread库)

【C++】多线程(基于Windows以及pthread库)

时间:2024-06-12 20:28:53浏览次数:33  
标签:函数 Windows void C++ cond 回收 pthread 线程 多线程

文章目录


一、 前言

在windows中进程只是一个容器,用于装载系统资源,它并不执行代码,它是系统资源分配的最小单元,而在进程中执行代码的是线程,线程是轻量级的进程,是代码执行的最小单位。

多线程的用处:

  • 任务分解:
    • 耗时的操作,任务分解,实时响应
  • 数据分解:
    • 充分利用多核CPU处理数据
  • 数据流分解:
    • 读写分流,解耦合设计

1.1 进程和线程

先从概念上了解一下线程和进程之间的区别:

  • 进程有自己独立的地址空间, 多个线程共用同一个地址空间

    • 线程更加节省系统资源, 效率不仅可以保持的, 而且能够更高
    • 在一个地址空间中多个线程独享: 每个线程都有属于自己的栈区, 寄存器(内核中管理的)
    • 在一个地址空间中多个线程共享: 代码段, 堆区, 全局数据区, 打开的文件(文件描述符表)都是线程共享的
  • 线程是程序的最小执行单位, 进程是操作系统中最小的资源分配单位

    • 每个进程对应一个虚拟地址空间,一个进程只能抢一个CPU时间片
    • 一个地址空间中可以划分出多个线程, 在有效的资源基础上, 能够抢更多的CPU时间片
    • 多线程之间是无序的状态,时间片是随机分配
      线程执行时间片分配
  • CPU的调度和切换: 线程的上下文切换比进程要快的多
    **上下文切换:**进程/线程分时复用CPU时间片,在切换之前会将上一个任务的状态进行保存, 下次切换回这个任务的时候, 加载这个状态继续运行,任务从保存到再次加载这个过程就是一次上下文切换。

  • 线程更加廉价, 启动速度更快, 退出也快, 对系统资源的冲击小。

二、 创建线程

一个程序至少有一个线程,这个线程称为主线程(main thread),如果我们不显示地创建线程,那我们产的程序就是只有主线程的间线程程序。

2.1 线程函数pthread_self(void)

每一个线程都有一个唯一的线程ID,ID类型为pthread_t,这个ID是一个无符号长整形数,如果想要得到当前线程的线程ID,可以调用如下函数:

pthread_t pthread_self(void);	// 返回当前线程的线程ID

在一个进程中调用线程创建函数,就可得到一个子线程,和进程不同,需要给每一个创建出的线程指定一个处理函数,否则这个线程无法工作。

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
// Compile and link with -pthread, 线程库的名字叫pthread, 全名: libpthread.so libptread.a

参数:

  • thread: 传出参数,是无符号长整形数,线程创建成功, 会将线程ID写入到这个指针指向的内存中
  • attr: 线程的属性, 一般情况下使用默认属性即可, 写NULL
  • start_routine: 函数指针,创建出的子线程的处理动作,也就是该函数在子线程中执行。
  • arg: 作为实参传递到 start_routine 指针指向的函数内部
  • 返回值:线程创建成功返回0,创建失败返回对应的错误号

2.2 创建线程

下面是创建线程的示例代码,在创建过程中一定要保证编写的线程函数与规定的函数指针类型一致:void *(*start_routine) (void *):

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

using namespace std;

// @file:CreateThreading
// @author:IdealSanX_T
// @date:2024/6/10 10:52:50
// @brief:创建线程Test

// 子线程的处理代码
void* working(void* arg){
    // 获取当前进程ID
    
    cout << "我是子线程, 线程ID:" << GetCurrentThreadId() << endl;
    for (int i = 0; i < 9; ++i){
        cout << "child == i: = " << i << endl;
    }
    return NULL;
}

int main(){
    // 1. 创建一个子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    Sleep(1000);
    // 2. 子线程不会执行下边的代码, 主线程执行
    cout << "我是主线程, 线程ID:" << GetCurrentThreadId() << endl;
    
    for (int i = 0; i < 3; ++i){
        cout << i << endl;
    }

    return 0;
}

这里如果报错,可以去看博主博客【C++】网络通信-Socket中的最后一章错误汇总

三、 线程退出

3.1 线程函数pthread_exit()

在编写多线程程序的时候,如果想要让线程退出,但是不会导致虚拟地址空间的释放(针对于主线程),我们就可以调用线程库中的线程退出函数,只要调用该函数当前线程就马上退出了,并且不会影响到其他线程的正常运行,不管是在子线程或者主线程中都可以使用。

#include <pthread.h>
void pthread_exit(void *retval);

参数: 线程退出的时候携带的数据,当前子线程的主线程会得到该数据。如果不需要使用,指定为NULL

四、 线程回收

4.1 线程函数pthread_join()

线程和进程一样,子线程退出的时候其内核资源主要由主线程回收,线程库中提供的线程回收函叫做pthread_join(),这个函数是一个阻塞函数,如果还有子线程在运行,调用该函数就会阻塞,子线程退出函数解除阻塞进行资源的回收,函数被调用一次,只能回收一个子线程,如果有多个子线程则需要循环进行回收。

另外通过线程回收函数还可以获取到子线程退出时传递出来的数据,函数原型如下:

#include <pthread.h>
// 这是一个阻塞函数, 子线程在运行这个函数就阻塞
// 子线程退出, 函数解除阻塞, 回收对应的子线程资源, 类似于回收进程使用的函数 wait()
int pthread_join(pthread_t thread, void **retval);

参数:

  • thread: 要被回收的子线程的线程ID
  • retval: 二级指针, 指向一级指针的地址, 是一个传出参数, 这个地址中存储了pthread_exit() 传递出的数据,如果不需要这个参数,可以指定为NULL

返回值:线程回收成功返回0,回收失败返回错误号。

4.2 线程数据回收

在子线程退出的时候可以使用pthread_exit()的参数将数据传出,在回收这个子线程的时候可以通过phread_join()的第二个参数来接收子线程传递出的数据。

通过函数pthread_exit(void * retval);可以得知,子线程退出的时候,需要将数据记录到一块内存中,通过参数传出的是存储数据的内存的地址,而不是具体数据,由因为参数是void*类型,所有这个万能指针可以指向任意类型的内存地址。

注意,如果返回的是子线程的局部变量信息时:如果多个线程共用同一个虚拟地址空间,每个线程在栈区都有一块属于自己的内存,相当于栈区被这几个线程平分了,当线程退出,线程在栈区的内存也就被回收了,因此随着子线程的退出,写入到栈区的数据也就被释放了。
所以在子线程使用pthread_join()返回数据时,需要使用全局变量或者主线程变量

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

using namespace std;

// @file:CreateThreading
// @author:IdealSanX_T
// @date:2024/6/10 10:52:50
// @brief:创建线程Test

// 子线程的处理代码
void* working(void* arg){
    // 接收参数
    int* a = (int*)arg;
    // 获取当  前进程ID
    cout << "我是子线程, 线程ID:" << GetCurrentThreadId() << endl;
    for (int i = 0; i < 9; ++i){
        cout << "child == i: " << i << endl;
    }
    
    *a = 10;
    // 该函数的参数将这个地址传递给了主线程的pthread_join()
    pthread_exit(a);
    return NULL;
}

int main(){
    int a;
    // 1. 创建一个子线程,并将a这个参数地址传入子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, &a);
    // 2. 子线程不会执行下边的代码, 主线程执行
    cout << "我是主线程, 线程ID:" << GetCurrentThreadId() << endl;

    // 阻塞等待子线程退出
    void* ptr = NULL;
    // ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存
    // 这个内存地址就是pthread_exit() 参数指向的内存
    pthread_join(tid, &ptr);
    // 做类型转换
    int* pt = (int*)ptr;
    cout << "子线程传出参数" << (*pt) << endl;

    // 主线程调用退出函数退出, 地址空间不会被释放
    pthread_exit(NULL);
    return 0;
}

五、 线程分离

5.1 线程函数pthread_detach()

在某些情况下,程序中的主线程有属于自己的业务处理流程,如果让主线程负责子线程的资源回收,调用pthread_join()只要子线程不退出主线程就会一直被阻塞,主要线程的任务也就不能被执行了。

在线程库函数中为我们提供了线程分离函数pthread_detach(),调用这个函数之后指定的子线程就可以和主线程分离,当子线程退出的时候,其占用的内核资源就被系统的其他进程接管并回收了。线程分离之后在主线程中使用pthread_join()就回收不到子线程资源了。

#include <pthread.h>
// 参数就子线程的线程ID, 主线程就可以和这个子线程分离了
int pthread_detach(pthread_t thread);

六、 C++线程类

C++线程类

七、线程同步

互斥量(锁)(Mutexes):

  • 创建:使用pthread_mutex_init()函数,指定互斥量属性(可选)。
  • 锁定:使用pthread_mutex_lock()函数,阻塞当前线程直至获得互斥量所有权。
  • 解锁:使用pthread_mutex_unlock()函数,释放互斥量所有权,可能唤醒等待该互斥量的其他线程。
  • 销毁:使用pthread_mutex_destroy()函数,释放互斥量占用的资源。

属性设置:通过pthread_mutexattr_init()、pthread_mutexattr_settype()等函数调整互斥量的属性,如是否为递归锁、是否为错误检查锁等。

以上参数均是指向互斥量的指针,pthread_mutex_init()函数第二个参数时互斥量属性,一般为NULL

条件变量(Condition Variables):

  • 原理:条件变量用于线程间的同步,允许线程在某个条件不满足时阻塞自己,当其他线程改变了该条件并通知等待的线程后,被阻塞的线程才能继续执行。
  • 使用场景:生产者-消费者模型、工作队列、多线程资源池等。

相关函数:

//初始化条件变量
pthread_cond_init( pthread_cond_t *cond,const pthread_condattr_t *attr)
  • pthread_cond_t *cond:指向 pthread_cond_t 类型的指针,用于接收新创建的条件变量。
  • const pthread_condattr_t *attr:指向条件变量属性的指针。如果为 NULL,则使用默认属性。
//阻塞当前线程,直到收到信号或超时。
pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex)
  • pthread_cond_t *cond:指向要等待的条件变量。
  • pthread_mutex_t *mutex:指向与条件变量配合使用的互斥锁。调用此函数之前,线程必须已经拥有这个互斥锁。
//唤醒一个等待该条件变量的线程。
 pthread_cond_signal(pthread_cond_t *cond)
  • pthread_cond_t *cond:指向要发送信号的条件变量。
//唤醒所有等待该条件变量的线程。
pthread_cond_broadcast(pthread_cond_t *cond)
  • pthread_cond_t *cond:指向要广播的条件变量。
//销毁条件变量。
pthread_cond_destroy(pthread_cond_t *cond)
  • pthread_cond_t *cond:指向要销毁的条件变量。

标签:函数,Windows,void,C++,cond,回收,pthread,线程,多线程
From: https://blog.csdn.net/qq_50921201/article/details/139573673

相关文章

  • DP经典问题----背包问题的代码实现(入门级)(C++/PYTHON)
    背包的状态转换方程i:表示物品序号j:表示背包大小W[i]:表示第i件物品的重量f[i,j]:表示在前i件物品中选择若干件放在承重为j的背包中,可以取得的最大价值f[i-1,j-Wi]:表示在前i-1件物品中选择若干件放在承重为j-Wi的背包中,可以取得的最大价值Pi(j>=Wi):表示第i件物品的价值,要......
  • Windows 服务器Nginx 下载、部署、配置流程(图文教程)
    不定期更新目录一、下载Nginx安装包二、上传安装包三、启动Nginx四、Nginx常用命令五、Nginx(最小)配置详解 六、Nginx(基础)配置详解七、反向代理八、负载均衡九、动静分离十、报错 一、下载Nginx安装包四个网址,根据自己的情况选择,我是用Nginx开源版的。1、下载......
  • 贪吃蛇c++
    来源5.贪吃蛇-从头开始编程_哔哩哔哩_bilibili我对代码进行了理解,并进行了改写,代码如下。因为水平有限,理解有误的地方,敬请指正。#include<iostream>#include<list>#include<thread>#include<Windows.h>usingnamespacestd;intnScreenWidth=120;intnScreenH......
  • python指南之多线程与多进程编程大全
    Python作为一种高级编程语言,提供了多种并发编程的方式,其中多线程与多进程是最常见的两种方式之一。在本文中,我们将探讨Python中多线程与多进程的概念、区别以及如何使用线程池与进程池来提高并发执行效率。多线程与多进程的概念多线程多线程是指在同一进程内,多个线程并发执......
  • 1188 有多少零-PAT乙级真题(2024夏季B-3)-极简代码-C++
    B-3有多少零给定 n 个正整数,请你数数它们的乘积的末尾有多少个零。例如26、225、48的乘积是280800,末尾有2个零。输入格式:输入给出一个不超过 10^6 的正整数 n,下一行给出 n 个不超过 10^6 的正整数。输出格式:在一行中输出给定的 n 个正整数的乘积末尾零的......
  • 1018 锤子剪刀布-PAT乙级真题-极简代码-C++
    大家应该都会玩“锤子剪刀布”的游戏:现给出两人的交锋记录,请统计双方的胜、平、负次数,并且给出双方分别出什么手势的胜算最大。输入格式:输入第1行给出正整数 N(≤105),即双方交锋的次数。随后 N 行,每行给出一次交锋的信息,即甲、乙双方同时给出的的手势。C 代表“锤子”、J......
  • 【力扣真题】3.哈希表|算法真题程序设计数据结构考研保研复试机试面试秋招春招蓝桥杯
    242.有效的字母异位词给定两个字符串s和t,编写一个函数来判断t是否是s的字母异位词。示例1:输入:s=“anagram”,t=“nagaram”输出:true示例2:输入:s=“rat”,t=“car”输出:false说明:你可以假设字符串只包含小写字母。力扣题目链接思......
  • Windows电脑安装Apache JMeter的详细教程
    前言作为一名测试工程师,性能测试是我们工作中不可或缺的一部分。而ApacheJMeter作为一款开源的性能测试工具,以其强大的功能和广泛的适用性,成为了许多测试工程师的首选。本篇文章将详细介绍如何在Windows电脑上安装JMeter。安装Java环境在安装JMeter之前,需要确保系统已经安装......
  • SSM-小区物业管理系统-48954(免费领源码+开发文档)可做计算机毕业设计JAVA、PHP、爬虫、
    基于SSM小区物业管理系统摘要随着计算机科学技术日渐成熟,人们已经深刻认识到了计算机功能的强大,计算机已经进入到了人类社会发展的各个领域,并且发挥着十分重要的作用。每个社区的物业管理是一项系统而复杂的工作,它需要一个团队互相配合、分工协作。在该领域,传统的手工存取......
  • windows server 2019 操作步骤和知识点(第一节)
    windowsserver1.1vmwareworkstation作用模拟硬件模拟操作系统步骤安装1模拟硬件文件新建虚拟机典型稍后安装操作系统Mcirosoftwindowswindows10X64win10-1d:/xujiji/win10-12模拟操作系统CD\DVD(SATA)使用ISO映像文件d:\iso\win10..........