C++多线程常用的几种实现方式

news2024/9/24 11:21:36

1、std::thread
C++11标准之后便引入了线程库std::thread。无论是windows或是Linux开发者都可以非常简单的通过这种方式,在C++程序中创建和管理线程。

示例代码:

#include <unistd.h>
#include <iostream>
#include <thread>

void thread_fun(void)
{
    std::cout << "thread_fun id: " << std::this_thread::get_id() << std::endl;
    sleep(1);
    std::cout << "thread_fun exit." << std::endl;
}

int main(int argc, char *argv[])
{
    std::cout << "main id: " << std::this_thread::get_id() << std::endl;

    std::thread testthread(thread_fun);
    testthread.join();

    std::cout << "main exit." << std::endl;

    return 0;
}

运行结果:
在这里插入图片描述
可以看到thread_fun处于另一个线程中执行。

且该子线程是一个阻塞的操作,主线程等待子线程返回后才继续往下执行,是因为我们对线程进行了join()操作,join()表示主线程要等待子线程返回后对其资源进行回收,所以它必须要等待子线程执行完成,才去执行join()后面的逻辑。因此,在实际的使用中调用join()的位置也是比较关键的。

当主线程不需要等待子线程的结果时,可以使用detach()分离操作。detach()会使子线程与主线程分离,不会阻塞主线程的运行,分离后便与主线程无关了,资源由系统回收。

示例代码:

#include <unistd.h>
#include <iostream>
#include <thread>

void thread_fun(void)
{
    std::cout << "thread_fun id: " << std::this_thread::get_id() << std::endl;
    sleep(1);
    std::cout << "thread_fun exit." << std::endl;
}

int main(int argc, char *argv[])
{
    std::cout << "main id: " << std::this_thread::get_id() << std::endl;

    std::thread testthread(thread_fun);
    testthread.detach();

    std::cout << "main exit." << std::endl;

    while (1)
    {
        pause();
    }

    return 0;
}

运行结果:
在这里插入图片描述
可以看到子线程没有阻塞主线程,detach()之后继续往下运行了。与join不同的是,在detach后面我们对主线程增加了while (1)的循环操作,防止主线程退出。如果不加循环等待会怎么样呢?

当把以上示例代码while (1)部分的代码删除,重新编译运行,会发现thread_fun子线程部分还没来得及执行,进程就直接退出了。
在这里插入图片描述
这是因为,detach()操作其实只是把子线程分离单独执行,而主线程的退出往往会伴随着整个进程退出,当进程退出时,所有的子线程也会随之终止。总的来说,即便子线程标记为detach()分离状态,也是与主线程“同生死,共存亡”的。

同时,C++标准库中也提供有一系列类库,用于实现多线程之间的数据同步及通信。如互斥锁std::mutex、条件变量std::condition_variable、互斥锁自动管理器std::lock_guard等。

2、pthread
对于Linux系统,pthread是Linux下的最常用的线程库,与std::thread不同,pthread是基于C语言实现。

有几个常用的数据类型及函数需要先了解一下:

pthread_t:标识线程ID。它是一个结构体数据类型,用于唯一标识一个线程。

pthread_attr_t:线程属性。可用于设置线程的堆栈地址、堆栈大小、优先级等。

pthread_mutex_t:互斥锁。用于线程同步,保护共享资源免受多个线程的同时访问。

pthread_create():创建一个线程。

pthread_self():获取当前线程ID。

pthread_join():阻塞当前的线程,直到另外一个线程运行结束。

pthread_detach():分离线程。类似于std::thread的detach()。

pthread_exit():终止当前线程。

pthread_attr_init():初始化线程属性。

pthread_attr_destroy():删除线程的属性。

pthread_attr_setschedparam():通过设置线程属性,设置线程优先级。

pthread_attr_setstacksize():通过设置线程属性,设置线程堆栈大小。

pthread_mutex_init():初始化互斥锁。

pthread_mutex_lock():互斥锁上锁。

pthread_mutex_unlock():互斥锁解锁。

pthread_mutex_destroy():销毁互斥锁。

以下通过一段示例代码,了解下pthread的常用用法:

#include <iostream>
#include <unistd.h>
#include <pthread.h>

static void *pthread_func(void *param)
{
  pthread_detach(pthread_self());                   //分离线程
  printf("pthread_func id=%ld\n", pthread_self());  //打印线程ID

  int count = 0;
  while(1)
  {
    sleep(2);
    printf("count=%d\n", count++);
  }
  return NULL;
}

int main(int argc, char *argv[])
{
  printf("main id=%ld\n", pthread_self());

  pthread_t p_tid;
  pthread_attr_t p_attr;
  sched_param param;
  param.sched_priority = 10;
  pthread_attr_init(&p_attr);                        //初始化线程属性
  pthread_attr_setschedparam(&p_attr, &param);       //设置线程优先级为10
  pthread_attr_setstacksize(&p_attr, 1024*1024);     //设置堆栈大小为1M
  pthread_create(&p_tid, &p_attr, pthread_func, NULL);  //创建线程

  while(1)
  {
    pause();
  }

  return 0;
}

在这里插入图片描述
通过以上代码便创建了一个线程,并将该线程与主线程分离,独立运行。需要注意的是,当需要把类成员函数作为线程函数的时候,需要把类成员函数声明为static静态函数才可。

当线程中涉及到数据共享时,就需要考虑到数据资源同步问题,这时就应该对同步的数据加锁处理。示例如下:

#include <iostream>
#include <unistd.h>
#include <pthread.h>

pthread_mutex_t m_mutex = PTHREAD_MUTEX_INITIALIZER;  //静态初始化
volatile int m_count = 0;

static void *pthread_func(void *param)
{
  pthread_detach(pthread_self());            //分离线程

  while(1)
  {
    sleep(1);
    pthread_mutex_lock(&m_mutex);
    printf("pthread_func id=%ld  m_count=%d\n", pthread_self(), m_count++);
    pthread_mutex_unlock(&m_mutex);
  }

  return NULL;
}

int main(int argc, char *argv[])
{
  printf("main id=%ld\n", pthread_self());

  //pthread_mutex_init(&m_mutex, NULL);          //动态初始化

  pthread_t p_tid;
  pthread_attr_t p_attr;
  sched_param param;
  param.sched_priority = 10;
  pthread_attr_init(&p_attr);              //初始化线程属性
  pthread_attr_setschedparam(&p_attr, &param);    //设置线程优先级为10
  pthread_attr_setstacksize(&p_attr, 1024*1024);     //设置堆栈大小为1M
  pthread_create(&p_tid, &p_attr, pthread_func, NULL);  //创建线程1
  pthread_create(&p_tid, &p_attr, pthread_func, NULL);  //创建线程2

  while(1)
  {
    pause();
  }

  return 0;
}

在这里插入图片描述
对于pthread_mutex_t互斥锁的初始化有两种方式,静态初始化和动态初始化。动态初始化即使用上述提及的pthread_mutex_init()函数,静态初始化则在编译时初始化锁为解锁状态。

3、CreateThread

CreateThread 函数是 Windows API 中用于创建新线程的一个非常重要的函数。在 Win32 编程中,线程是系统分配处理器时间资源的基本单元,它是进程中的实际运作单位。通过使用 CreateThread可以在同一进程中创建多个线程。
CreateThread 函数原型如下:

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(表示线程创建后立即运行)或CREATE_SUSPENDED(表示线程创建后暂停运行,直到调用ResumeThread函数)。

lpThreadId:指向DWORD变量的指针,用于接收新线程的标识符。如果为NULL,则不返回线程标识符。

返回值:
如果函数执行成功,CreateThread 返回一个指向新线程的句柄。如果函数调用失败,则返回值为 NULL。

示例代码:

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

DWORD WINAPI ThreadFunc(LPVOID lpParam)
{
    printf("Hello!!!\n");
    return 0;
}

int main(int argc, char *argv[])
{  
    HANDLE hThread;  
    DWORD threadId;  
  
    //创建线程 
    hThread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, &threadId);  
  
    if (hThread == NULL)
    {  
        printf("创建线程失败 (%d)\n", GetLastError());  
        return -1;
    }
    //等待线程结束
    WaitForSingleObject(hThread, INFINITE);
  
    //关闭线程句柄
    CloseHandle(hThread);

    return 0;  
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1966346.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

App 自动化测试调研

App 自动化测试调研 App 自动化测试的价值 App 自动化测试在软件开发过程中扮演着重要的角色&#xff0c;具有以下几个方面的价值&#xff1a; 1.提高测试效率和覆盖率&#xff1a;自动化测试可以执行大量的测试用例&#xff0c;覆盖各种功能和场景&#xff0c;相比手动测试…

BootStrapBlazor中MultiSelect组件在表格中使用时存在的问题

仅作记录&#xff0c;备以后回忆 在Table中使用了双击弹出编辑框来修改数据&#xff0c;正好逆变器需要多选&#xff0c;就使用了MultiSelect组件&#xff0c;结果发现它选择后没有任何效果&#xff0c;不会显示&#xff0c;但是保存后发现它是有数据的&#xff0c;网上搜了下…

关于python问题 ,生成的excel文件内无爬取的数据存在,请问应如何解决?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

深度分析 !中国 AI 产业商业化实践案例 ! 2024 !

提供客户全渠道全生命周期的营服销一体化智能服务 京东云言犀依托于全栈自研的人工智能技术&#xff0c;基于京东集团广泛实体业务、庞大而又复杂的产业生态&#xff0c;从内部真实、复杂的海量业务场景实践中推出千亿级参数的言犀大模型&#xff0c;打造全新的智能交互与生成…

中国人民解放军建军97周年

缅怀先烈&#xff0c;砥砺前行 付吾辈之韶华&#xff0c;耀吾辈之中华! 万里河山&#xff0c;有您心安!

单链表习题(3)(超详细)

前言&#xff1a; 这篇文章将会是小编最近做过的习题总结的最后一篇&#xff0c;这一篇有些习题颇有一些难度&#xff0c;不过小编将会带领读者朋友们一起克服难关&#xff0c;下面废话不多说&#xff0c;开启今天的习题之旅&#xff01; 目录&#xff1a; 1.链表分割 2.相交链…

复习Nginx

1.关于Nginx Nginx的关键特性 1.支持高并发 2.内存资源消耗低 3.高扩展性&#xff08;模块化设计&#xff09; 4.高可用性&#xff08;master-worker&#xff09; Nginx运行架构 注意 默认情况下&#xff0c;Nginx会创建和服务器cpu核心数量相等的worker进程 worker进程之间…

自从用了这些监控工具,我连续几天没睡好觉!

大家好&#xff0c;我是程序员鱼皮&#xff0c;今天分享一些很实用的系统监控告警工具。 为什么要用监控告警&#xff1f; 说到监控告警&#xff0c;没有企业开发经验的同学非常容易忽视它&#xff0c;甚至会有同学觉得没有必要&#xff0c;大不了出了 Bug 再修就是了。 这种…

MySQL:初识数据库初识SQL创建数据库

目录 1、初识数据库 1.1 什么是数据库 1.2 什么是MySQL 2、数据库 2.1 数据库服务&数据库 2.2 C/S架构 3、 初识SQL 3.1 什么是SQL 3.2 SQL分类 4、使用SQL 4.1 查看所有数据库 4.1.2 语句解析 4.2 创建数据库 4.2.1 if not exists校验 4.2.2 手动明确字符集…

第33篇 计算数据中最长的连续1的个数<三>

Q&#xff1a;如何将计算出的结果&#xff08;最长的连续1的个数&#xff09;显示在DE2-115开发板的HEX上&#xff1f; A&#xff1a;基本原理&#xff1a;DE2-115_Computer_System中的HEX并行端口作为内存映射设备连接到DE2-115开发板的七段数码管&#xff0c;每个端口都对应…

uniapp微信小程序按钮分享定制动态传参

打印 onShareAppMessage 中的传参&#xff08;注意&#xff1a;上方我定义了一个open-type为share的按钮&#xff09; 打印之后如下&#xff1a; 给按钮绑定 data-id 可以在这里的 dataset 中取到 然后根据 from 的值来动态返回分享的参数 示例代码&#xff1a; <…

通过材质节点Panner实现球体自转

纹理坐标的移动&#xff0c;可以达到球体自转的功能&#xff0c;从而减少代码的复杂度。由于是绕着一个轴转动&#xff0c;所以在u方向移动即可。 材质&#xff1a; 材质实例 运行如下&#xff1a;

怎么把视频转换成mp4格式?分享几款视频转换软件

在数字化时代&#xff0c;视频已经成为我们日常生活和工作中的重要组成部分。然而&#xff0c;不同的设备和软件可能支持不同的视频格式&#xff0c;这常常导致兼容性问题。MP4作为一种广泛支持且高质量的视频格式&#xff0c;经常被用作解决这类问题的首选方案。那么&#xff…

医疗器械维修培训哪里好

医疗器械维修培训哪里好 医学技术的应用离不开医疗设备的支持,近几年医疗行业发展迅速&#xff0c;城镇、县级、市级医院设备也都越来越多&#xff0c;医疗机构拥有大量的医疗设备&#xff0c;需要专业的维修团队来保障设备的正常运行。医疗设备的论证、验收、使用、维护、维修…

多语言海外AEON抢单可连单加额外单源码,java版多语言抢单系统

多语言海外AEON抢单可连单加额外单源码&#xff0c;java版多语言抢单系统。此套是全新开发的java版多语言抢单系统。 后端java&#xff0c;用的若依框架&#xff0c;这套代码前后端是编译后的&#xff0c;测试可以正常使用&#xff0c;语言繁体&#xff0c;英文&#xff0c;日…

WebSocket 协议与 HTTP 协议、定时轮询技术、长轮询技术

目录 1 为什么需要 WebSocket&#xff1f;2 WebSocket2.1 采用 TCP 全双工2.2 建立 WebSocket 连接2.3 WebSocket 帧 3 WebSocket 解决的问题3.1 HTTP 存在的问题3.2 Ajax 轮询存在的问题3.3 长轮询存在的问题3.4 WebSocket 的改进 参考资料&#xff1a; 为什么有 h…

leetcode提示LeetCode extension needs Node.js installed in environment path

背景 系统&#xff1a;mac node版本 已通过nvm安装了多个版本node&#xff0c;并通过nvm alias default XX指定了node默认版本 描述&#xff1a;vscode安装了leetcode后&#xff0c;提示&#xff1a;LeetCode extension needs Node.js installed in environment path 问题…

leetcode958. 二叉树的完全性检验,层序遍历的巧用

leetcode958. 二叉树的完全性检验 给你一棵二叉树的根节点 root &#xff0c;请你判断这棵树是否是一棵 完全二叉树 。 在一棵 完全二叉树 中&#xff0c;除了最后一层外&#xff0c;所有层都被完全填满&#xff0c;并且最后一层中的所有节点都尽可能靠左。最后一层&#xff0…

Web安全学习顺序:从零到精通的指南

随着互联网的迅猛发展&#xff0c;Web安全已成为一个日益重要的领域。无论是企业还是个人&#xff0c;都需要关注并提升自身的Web安全防护能力。对于初学者而言&#xff0c;如何系统地学习Web安全知识&#xff0c;掌握相关技能&#xff0c;成为了一个亟待解决的问题。本文将为你…

C# 串口通信(通过serialPort控件发送及接收数据)

连接串口 界面设计打开串口发送数据通过文件发送发送数据 接收数据 首先可以在 工具箱中搜索serialport&#xff0c;将控件拖到你的Winfrom窗口。 界面设计 打开串口 private void Connect_Click(object sender, EventArgs e){serialPort1.PortName comboBox2.Text;//端口名s…