当前位置: 首页 > news >正文

c++线程

pthread(部分内容来自菜鸟教程)

创建线程

创建一个 POSIX 线程:

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg) 

pthread_create 创建一个新的线程,并让它可执行。

参数:

  • thread :指向线程标识符指针
  • attr :一个不透明的属性对象,可以被用来设置线程属性。可以指定线程属性对象,也可以使用默认值 NULL。
  • start_routine :线程运行函数起始地址,一旦线程被创建就会执行。
  • arg :运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。

终止线程

#include <pthread.h>
pthread_exit (status) 

向线程传递参数

在线程回调中传递任意的数据类型,因为它指向 void。

使用 pthread_create() 函数创建了 5 个线程,并接收传入的参数。每个线程打印一个 “Hello Runoob!” 消息,并输出接收的参数,然后调用 pthread_exit() 终止线程。

#include <iostream>
#include <cstdlib>
#include <pthread.h>using namespace std;#define NUM_THREADS     5struct thread_data{int  thread_id;char *message;
};void *PrintHello(void *threadarg)
{struct thread_data *my_data;my_data = (struct thread_data *) threadarg;cout << "Thread ID : " << my_data->thread_id ;cout << " Message : " << my_data->message << endl;pthread_exit(NULL);
}int main ()
{pthread_t threads[NUM_THREADS];struct thread_data td[NUM_THREADS];int rc;int i;for( i=0; i < NUM_THREADS; i++ ){cout <<"main() : creating thread, " << i << endl;td[i].thread_id = i;td[i].message = (char*)"This is message";rc = pthread_create(&threads[i], NULL,PrintHello, (void *)&td[i]);if (rc){cout << "Error:unable to create thread," << rc << endl;exit(-1);}}pthread_exit(NULL);
}

连接和分离线程

pthread_join (threadid, status) 
pthread_detach (threadid) 

pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连接。

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>using namespace std;#define NUM_THREADS     5void *wait(void *t)
{int i;long tid;tid = (long)t;sleep(1);cout << "Sleeping in thread " << endl;cout << "Thread with id : " << tid << "  ...exiting " << endl;pthread_exit(NULL);
}int main ()
{int rc;int i;pthread_t threads[NUM_THREADS];pthread_attr_t attr;void *status;// 初始化并设置线程为可连接的(joinable)pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);for( i=0; i < NUM_THREADS; i++ ){cout << "main() : creating thread, " << i << endl;rc = pthread_create(&threads[i], NULL, wait, (void *)&i );if (rc){cout << "Error:unable to create thread," << rc << endl;exit(-1);}}// 删除属性,并等待其他线程pthread_attr_destroy(&attr);for( i=0; i < NUM_THREADS; i++ ){rc = pthread_join(threads[i], &status);if (rc){cout << "Error:unable to join," << rc << endl;exit(-1);}cout << "Main: completed thread id :" << i ;cout << "  exiting with status :" << status << endl;}cout << "Main: program exiting." << endl;pthread_exit(NULL);
}

同步实现–锁

lock有很多实现的方式,linux这里使用的是mutex,由于mutex本身是结构体,所以声名的时候别忘了init一下。

#include "pthread.h"pthread_mutex_t mutex; //这里要保证是global var,
pthread_mutex_init(&mutex, NULL); //init mutex,pthread_mutex_lock(&mutex); //准备进入临界区// critical section //pthread_mutex_unlock(&mutex); //释放锁,别的线程可能要使用
pthread_mutex_destroy(&mutex); // 销毁锁,锁不能在用了

Condition vatiables(cv)

cv的含义其实有点偏向于MPI的思想,传递消息;可以让threads wait,也可以notify or broadcast线程让他们起来干活。主要的操作有一下三种:

phtread_cond_init();pthread_cond_wait(&theCV,&somelock); //这里sleep线程的同时也将somelock释放掉了,要不其他线程无法取得lock就没办法执行(甚至是叫醒它了)
pthread_cond_signal(&theCV); 
pthread_cond_boardcast(&theCV);

在临界区中处理写share量的时候,需要保证不同线程对其的访问是可控的,否则可能在不同线程中读取到的写share量不一致进而影响CV的工作,因为一般情况下临界区中的写share量就是我们CV工作中的重要判断量。因此,虽然这个条件相对严格,但是是有必要的。

semaphore

这个东西可以看作是lock的一个自然延申。也就是一个资源可以同时被多少执行单元使用。我们之前讲到的lock就可以看做是一个binary semaphore。这里就只是简要的谈谈,因为这个东西使用的时候很让人头大,弄不好就会死锁。而且虽然semaphore属于POSIX标准,但是严格来讲的话,它不属于pthread。

#include <semaphore.h>sem_t sem;
sem_init(&sem);
sem_wait(&sem);// critical sectionsem_post(&sem);
sem_destroy(&sem);

semaphore可以控制同一个时间有多少thread可以访问临界区,需要注意的就是上面声明–释放的匹配关系不要忘记。

std::thread

创建线程

通过 detach() 函数,将子线程和主线分离,子线程可以独立继续运行,即使主线程结束,子线程也不会结束。

#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;void test() {cout << "Hello World" << endl;
}int main() {std::thread t1(test);t1.detach();this_thread::sleep_for(10ms); // c++ 17// 低于C++17使用这行代码  this_thread::sleep_for(chrono::milliseconds(10));return 0;
}

传递参数

join() 函数可以在当前线程等待线程运行结束。

#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;void test(int Id,string name) {cout << "Hello World" << endl;cout << "Id : " <<Id<<" Name : "<<name<< endl;
}int main() {std::thread t1(test,12,"haha");t1.join();this_thread::sleep_for(10ms); // c++ 17// 低于C++17使用这行代码  this_thread::sleep_for(chrono::milliseconds(10));return 0;
}

线程休眠

using namespace std::literals::chrono_literals;
// 让当前线程睡眠 10 毫秒
this_thread::sleep_for(10ms);
// 低于C++17使用这行代码  this_thread::sleep_for(chrono::milliseconds(10));
// 让当前线程睡眠 5 秒
this_thread::sleep_for(5s);

condition_variable

使用 condition_variable 实现生产者和消费者的实验,通过 wait 进入线程等待,知道有其它的线程把当前线程唤醒。

当 std::condition_variable 对象的某个 wait 函数被调用的时候,它使用 std::unique_lock(通过 std::mutex) 来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的 std::condition_variable 对象上调用了 notification 函数来唤醒当前线程。

当前线程调用 wait() 后将被阻塞(调用wait()前先获取线程锁std::unique_lockstd::mutex lock(g_mutex))同时会自动调用 unlock() 释放锁(g_mutex),直到另外某个线程调用 notify_*(g_con.notify_one()、g_con.notify_all()) 唤醒了当前线程。唤醒当前线程时wait() 函数也是自动调用 lock()(g_mutex:加锁,进入临界区),使得 g_mutex 的状态和 wait 函数被调用时相同。

在线程被阻塞时,该函数会自动调用 lck.unlock() 释放锁,使得其他被阻塞在锁竞争上的线程得以继续执行。另外,一旦当前线程获得通知(notified,另外某个线程调用 notify_* 唤醒了当前线程),wait() 函数也是自动调用 lck.lock(),使得 lck 的状态和 wait 函数被调用时相同。

#include <iostream>
#include <thread>
#include <list>
using namespace std::literals::chrono_literals;
using namespace std;std::mutex g_mutex; // 线程锁
condition_variable g_con; // cvlist<int> products;void test() {int product_id = 0;while (true) {products.push_back(++product_id);cout << "products 生产: " << product_id << endl;std::unique_lock<std::mutex> lock(g_mutex);  // 进入临界区// 通知消费者消费g_con.notify_one(); 						// 唤醒 g_con.wait(lock)// g_con.notify_all(); // 唤醒所有线程(多个线程同时使用 g_con g_mutex)lock.unlock(); 								// 提前释放锁,供其他线程进入临界区if (product_id > 50) {break;}this_thread::sleep_for(2ms);}
}int main() {std::thread t1(test);while (true) {std::unique_lock<std::mutex> lock(g_mutex); // 离开代码作用域后自动解锁if (products.empty()) {cout << "没有产品,等待" << endl;// 进入等待,知道有新产品g_con.wait(lock);  // 释放锁 线程阻塞;唤醒时,自动加锁,与调用wait函数之前一行if(!products.empty()){int product_id = products.front();products.pop_front();cout << "消费产品 " << product_id << endl;this_thread::sleep_for(2ms);if (product_id > 50) break;                }}}t1.join();return 0;
}

std::unique_lock<std::mutex> 出作用域后会自动解锁

thread_local

C++11中提供了thread_local,thread_local定义的变量在每个线程都保存一份副本,而且互不干扰,在线程退出的时候自动销毁。

#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;
thread_local int t_l_counter = 0;void test() {cout << "flag1 t_l_counter: " << t_l_counter << endl;t_l_counter = 2;
}int main() {t_l_counter = 1;std::thread t1(test);t1.join();cout << "flag2 t_l_counter: " << t_l_counter << endl;return 0;
}

同步锁

如果需要同一时间只有一个线程在test函数中执行代码,那么就要加锁,lock() 用于加锁,而unlock() 解锁。

#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;
std::mutex g_mutex;void test() {g_mutex.lock();cout << "task start thread ID: " << this_thread::get_id() << endl;this_thread::sleep_for(10ms);cout << "task end thread ID: " << this_thread::get_id() << endl;g_mutex.unlock();
}
int main() {std::thread t1(test);std::thread t2(test);std::thread t3(test);t1.join();t2.join();t3.join();return 0;
}

除了std::mutex(非递归的互斥量),还有std::timed_mutex(带超时的非递归互斥量),std::recursive_mutex(递归互斥量)、std::recursive_timed_mutex(带超时的递归互斥量)。

lambda在线程中的使用

lambda 的语法

[capture](parameters) mutalble->return-type{statement};

编译器会自动生成一个匿名类,该类重载了 () 运算符。

int id = 0;
auto f = [id]() mutable {cout << "id: " << id << endl;++id;
}

capture

  • [] :什么也不捕获
  • [=] : 按值的方式捕获所有变量
  • [&] : 按引用方式捕获所有变量
  • [boo] : 值捕获boo的值
  • [=,&a] : 按值捕获所有局部变量,按引用捕获变量a
  • [=,&a,&b,&c] : 同上
  • [&,a] : 按引用捕获所有局部变量,按值捕获方式捕获a
  • [&,a,b,c] : 同上
  • [this] : 在成员函数中,直接捕获this指针

mutable

值捕获后,在匿名函数中对该值是不能做修改的,如果想要做修改,必须加上 mutable 关键字,并且在匿名函数中做的修改结果在函数外是不会生效的。

parameters

参数列表也是可以将外部的值传递给匿名函数内部的;与正常函数的形参一样。

return-type

对于编译器能自动推导的返回类型,可以省略 return-type,但是如果无法推导的类型,就必须添加上返回类型

当函数不止一个return语句时,就需要加上返回类型了

线程中使用lambda

#include <iostream>
#include <thread>
using namespace std;
int main() {std::thread t1([]{cout << "task start thread ID: " << this_thread::get_id() << endl;}};t1.join();return 0;
}

相关文章:

c++线程

pthread(部分内容来自菜鸟教程) 创建线程 创建一个 POSIX 线程&#xff1a; #include <pthread.h> pthread_create (thread, attr, start_routine, arg) pthread_create 创建一个新的线程&#xff0c;并让它可执行。 参数&#xff1a; thread &#xff1a;指向线程标…...

【Docker】02-安装mysql

参考教程&#xff1a; https://www.bilibili.com/video/BV1Qa4y1t7YH/?p5&spm_id_frompageDriver&vd_source4964ba5015a16eb57d0ac13401b0fe77 docker安装Mysql 1、拉取最新版本的镜像 docker pull mysq:latestl 2、运行mysql服务 docker run --name mysql -e MYSQL_…...

JAVA每日小知识(关于excel下载时插入和stream流遍历优化)

1、在windows系统下启动rocketmq操作&#xff1a; 在bin目录下使用cmd 分别输入 start mqnamesrv.cmd start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnabletrue 2、在stream流中需要new对象时&#xff0c;可能会出现new很多对象堆积在堆中&#xff0c;这是需要用try,…...

阿里后端开发:抽象建模经典案例

0.引言 在互联网行业&#xff0c;软件工程师面对的产品需求大都是以具象的现实世界事物概念来描述的&#xff0c;遵循的是人类世界的自然语言&#xff0c;而软件世界里通行的则是机器语言&#xff0c;两者间跨度太大&#xff0c;需要一座桥梁来联通&#xff0c;抽象建模便是打…...

【车载以太网测试从入门到精通】——DoIP BootLoader刷写测试(含CAPL源码)

系列文章目录 文章目录 系列文章目录前言一、DoIP刷写环境搭建二、DoIP刷写工程使用方法三、DoIP刷写CAPL源码四、刷写工程下载链接前言 DoIP概述: DoIP(Diagnostic communication over InternetProtocol),基于IP网络的汽车诊断协议。DoIP技术可实现本地诊断、远程诊断、空…...

RK开发板的USB连接(Ubuntu)

一、安装连接工具 sudo apt-get install putty 二、启动putty工具 sudo putty 三、连接usb&#xff0c;并查看相关的信息 # 查看接入的是否有usb ls /dev/tty* 显示如下&#xff1a;&#xff08;含有usb接口&#xff1a; /dev/ttyUSB0&#xff09; /dev/tty /dev/tty23 /d…...

Redis-Cluster集群的部署(详细步骤)

一、环境准备 本次实操为三台机器&#xff0c;关闭防火墙和selinux 注:规划架构两种方案&#xff0c;一种是单机多实例&#xff0c;这里我们采用多机器部署 三台机器&#xff0c;每台机器上面两个redis实例&#xff0c;一个master一个slave&#xff0c;第一列做主库&#xff…...

Vulnhub: Hogwarts: Bellatrix靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.228 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.228访问80端口 查看源码&#xff0c;提示ikilledsiriusblack.php和文件包含的参数名file 漏洞利用 ikilledsiriusblack.p…...

机器学习(吴恩达第一课)

课程链接 文章目录 第一周1、机器学习定义2、监督学习(Supervised learning)1、回归(Regression)2、分类(Classification) 3、无监督学习(Unsupervised learning)4、线性回归模型5、代价函数6、梯度下降(Gradient descent)1、学习率2、用于线性回归的梯度下降 第二周(多维特征…...

固定资产管理怎么改革

固定资产管理改革需要考虑以下几个方面&#xff1a; 建立完善的管理制度和流程&#xff1a;制定固定资产管理的规章制度&#xff0c;明确各部门的职责和任务&#xff0c;规范资产采购、登记、领用、保管、维修、报废等流程。 采用先进的资产管理软件&#xff1a;通过采用先进的…...

【js】防抖和节流的使用场景和区别:

文章目录 一、防抖 &#xff08;多次触发 只执行最后一次&#xff09;二、节流 &#xff08;规定时间内 只触发一次&#xff09;三、防抖和节流的使用场景【1】防抖&#xff08;debounce&#xff09;【2】节流&#xff08;throttle&#xff09; 一、防抖 &#xff08;多次触发 …...

Blazor前后端框架Known-V1.2.14

V1.2.14 Known是基于C#和Blazor开发的前后端分离快速开发框架&#xff0c;开箱即用&#xff0c;跨平台&#xff0c;一处代码&#xff0c;多处运行。 Gitee&#xff1a; https://gitee.com/known/KnownGithub&#xff1a;https://github.com/known/Known 概述 基于C#和Blazo…...

港陆证券:五日线破位怎么看?

在股票交易中&#xff0c;五日线是个重要的技术指标之一&#xff0c;它能够反映出最近的商场趋势。假如五日线破位&#xff0c;这意味着商场呈现了趋势反转&#xff0c;出资者需求注重趋势改动&#xff0c;并采取相应的出资战略。 首先&#xff0c;咱们来看看五日线破位的原因…...

睿趣科技:抖音小店多久可以做起来

随着社交媒体的迅猛发展&#xff0c;抖音成为了全球最受欢迎的短视频平台之一&#xff0c;吸引了数以亿计的用户。在抖音上&#xff0c;人们不仅可以分享自己的生活、才艺和创意&#xff0c;还可以创业经营抖音小店。但是&#xff0c;很多人都想知道&#xff0c;一个抖音小店到…...

onnx 模型切割掉conv后面的节点,设置输出层名称和最后节点名称一致,设置输出层shape和输出节点一致.

某些模型最后卷积层之后的算子不适合在推理引擎里面跑&#xff0c;切割掉conv后面的算子&#xff0c;在cpu上实现有比较好的性能&#xff0e; 包含&#xff1a; &#xff11;&#xff0e;获取onnx中间节点的shape的示例 &#xff12;&#xff0e;增加onnx模型输出&#xff0c;设…...

泛型的学习

泛型深入 泛型&#xff1a;可以在编译阶段约束操作的数据类型&#xff0c;并进行检查 泛型的格式&#xff1a;<数据类型> 注意&#xff1a;泛型只能支持引用数据类型 //没有泛型的时候&#xff0c;集合如何存储数据//如果我们没有给集合指定类型&#xff0c;默认认为…...

L1-061 新胖子公式(Python实现) 测试点全过

前言&#xff1a; {\color{Blue}前言&#xff1a;} 前言&#xff1a; 本系列题使用的是&#xff0c;“PTA中的团体程序设计天梯赛——练习集”的题库&#xff0c;难度有L1、L2、L3三个等级&#xff0c;分别对应团体程序设计天梯赛的三个难度。更新取决于题目的难度&#xff0c;…...

潜艇来袭(Qt官方案例-2维动画游戏)

一、游戏介绍 1 开始界面 启动程序&#xff0c;进入开始界面。 2 开始新游戏 点击菜单&#xff1a;File》New Game &#xff08;或者CtrlN&#xff09;进入新游戏。 开始新游戏之后&#xff0c;会有一个海底的潜艇&#xff0c;和水面舰艇对战。 计算机&#xff1a;自动控制…...

50ETF期权开户平台(0门槛期权开户指南)

50ETF期权开户平台比较好的有&#xff1a;期权馆&#xff0c;期权科普馆&#xff0c;小熊期权&#xff0c;期权酱&#xff0c;财顺财经&#xff0c;财顺期权等&#xff0c;都是国内前十的期权分仓平台&#xff0c;下文为大家结算50ETF期权开户平台&#xff08;0门槛期权开户指南…...

leaflet · 关于轨迹移动

1.引入 import MovingMarker from "../src/utils/MovingMarker"; 2.MovingMarker.js内容 import L from "leaflet"; import eventBus from ../util/eventBus; L.interpolatePosition function(p1, p2, duration, t) {var k t/duration;k (k > 0) ? …...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...