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;
}

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

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

相关文章

Opencv 图像金字塔----高斯和拉普拉斯

原文&#xff1a;图像金字塔----高斯和拉普拉斯 图像金字塔是图像中多尺度表达的一种&#xff0c;最初用于机器视觉和图像压缩&#xff0c;最主要用于图像的分割、融合。 高斯金字塔 ( Gaussian pyramid): 高斯金字塔是由底部的最大分辨率图像逐次向下采样得到的一系列图像…

基于docker快速搭建facechain环境

前言 最近facechain比较火&#xff0c;之前在huggingface试过&#xff0c;在SD的落地场景上提供了思路。 这两天刚拿到一台RTX3090的服务器&#xff0c;在本地部署也遇到了两个问题&#xff0c;给大家分享一下。 一、facechain是什么&#xff1f; 官方是这样说的&#xff1a…

(九)mmdetection源码解读:训练过程中训练数据的调用DataLoader

目录 一、DataLoader创建过程中二、利用实例化data_loaders进行训练 一、DataLoader创建过程中 在训练过程train_detector函数中调用build_dataloader函数 train_detector(model, datasets, cfg, distributedFalse, validateTrue) #train_detector函数中 data_loaders [buil…

# 磁盘引导方式相关知识之BIOS、msdos、MBR、UEFI、gpt、esp、csm

磁盘引导方式相关知识之BIOS、msdos、MBR、UEFI、gpt、esp、csm 磁盘、分区、引导等知识经常似懂非懂&#xff0c;不能完全说清楚&#xff0c;梳理下&#xff1a; 序号主板芯片引导方式支持的磁盘分区表类型支持的磁盘分区表格式对应引导位置备注1BIOS传统方式&#xff08;俗…

我的区块链笔记

区块链 中心化的账本&#xff0c;个人节点和中心节点的地位不对等&#xff0c;中心节点说了算。去中心化&#xff0c;个人节点就是公平的&#xff0c;根据一套规则&#xff0c;叫做公比机制。 区块链的本质&#xff0c;就是数据存储方式 区块链使用密码学算法产生的区块&…

利用AI技术提升乳腺癌诊断准确率

背景&#xff1a; 乳腺癌是全球女性最常见的癌症之一&#xff0c;早期诊断和治疗对提高治愈率和生存率至关重要。传统的乳腺X光检查和病理学诊断方法存在一定的误诊和漏诊率。近年来&#xff0c;人工智能技术在医学领域得到了广泛应用&#xff0c;为提升乳腺癌诊断准确率提供了…

企微SCRM营销平台MarketGo-ChatGPT助力私域运营

一、前言 ChatGPT是由OpenAI&#xff08;开放人工智能&#xff09;研发的自然语言处理模型&#xff0c;其全称为"Conversational Generative Pre-trained Transformer"&#xff0c;即对话式预训练转换器。它是GPT系列模型的最新版本&#xff0c;GPT全称为"Gene…

指针进阶(一)

指针进阶 1. 字符指针面试题 2. 指针数组3. 数组指针3.1 数组指针的定义3.2 &数组名VS数组名 3.3 数组指针的使用4. 数组传参和指针传参4.1 一维数组传参4.2 二维数组传参4.3 一级指针传参4.4 二级指针传参 前言 指针的主题&#xff0c;我们在初级阶段的《指针》章节已经接…

在Windows下设置将EXE开机自启动

在Windows下设置将EXE开机自启动&#xff0c;有多种方法。以下是两种常用的方法&#xff1a; 方法一&#xff1a;通过注册表 打开“运行”&#xff08;快捷键&#xff1a;Win R&#xff09;&#xff0c;输入&#xff1a;reg add HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windo…

NLP(2)--Transformer

目录 一、Transformer概述 二、输入和输出 三、Encoder 四、Decoder 五、正则化处理 六、对于结构的改进&#xff1f; 七、AT vs NAT 八、Cross-attention 一、Transformer概述 Transformer模型发表于2017年Google团队的Attention is All you need这篇论文&#xff0c;…

蓝桥杯打卡Day1

文章目录 全排列八皇后 一、全排列IO链接 本题思路:本题是一道经典的全排列问题&#xff0c;深度优先搜索即可解决。 #include <bits/stdc.h>constexpr int N10;std::string s; std::string ans; int n; bool st[N];void dfs(int u) {if(un){std::cout<<ans<…

【计算机视觉 | 图像分割】arxiv 计算机视觉关于图像分割的学术速递(8 月 29 日论文合集)

文章目录 一、分割|语义相关(20篇)1.1 VideoCutLER: Surprisingly Simple Unsupervised Video Instance Segmentation1.2 Compositional Semantic Mix for Domain Adaptation in Point Cloud Segmentation1.3 Referring Image Segmentation Using Text Supervision1.4 Semi-Sup…

kafka知识小结

1.为什么分区数只能增加,不能减少? 按照Kafka现有的代码逻辑而言,此功能完全可以实现,不过也会使得代码的复杂度急剧增大。 另外实现此功能需要考虑的因素很多,比如删除掉的分区中的消息该作何处理? 如果随着分区一起消失则消息的可靠性得不到保障; 如果需要保留则又需…

前后端数据加密传输基于AES+RSA实现

前后端数据加密传输基于AESRSA实现 什么是AES和RSA AES AES&#xff08;Advanced Encryption Standard&#xff09;是一种对称加密算法&#xff0c;它的加密速度快&#xff0c;安全性也比较高&#xff0c;是目前广泛使用的加密算法之一。AES的密钥长度可以选择128位、192位和…

NetSuite海鲜书 - 知识会汇编 用户篇 2023

NetSuite2021年初夏&#xff0c;NetSuite知识会成立。它由本人&#xff0c;上海德之匠信息技术有限公司的毛岩喆&#xff08;江湖人称Rick&#xff09;发起建立。建立的初衷秉承Rick个人博客“学问思辨&#xff0c;企业信息化路上的行者”的理念&#xff0c;期望能够在NetSuite…

Flink基础

Flink architecture job manager is master task managers are workers task slot is a unit of resource in cluster, number of slot is equal to number of cores(超线程则slot2*cores), slot一组内存一些线程共享CPU when starting a cluster,job manager will allocate a …

docker快速安装-docker一键安装脚本

1.下载/配置安装脚本 touch install-docker.sh #!/bin/bash #mail:ratelcloudqq.com #system:centos7 #integration: docker-latestclear echo "######################################################" echo "# Auto Install Docker …

使用 skopeo 批量同步 helm chart 依赖镜像

skopeo 是什么&#xff1f; skepeo 是一个开源的容器镜像搬运工具&#xff0c;比较通用&#xff0c;各种镜像仓库都支持。 安装 skopeo 参考官方的 安装指引。 导出当前 helm 配置依赖哪些镜像 $ helm template -n monitoring -f kube-prometheus-stack.yaml ./kube-prome…

大数据组件-Flume集群环境搭建

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

Mybatis对数据加密解密(AES加解密)操作

1&#xff1a;使用mybatis的BaseTypeHandler类&#xff0c;编写需要加密解密处理的类型 package com.example.poi.typeHandlers;import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.symmetric.AES; import org.apache.ibatis.type.BaseTypeHandler; import org.apa…