C++11实现线程池

news2024/11/18 4:24:11

1.所有权的传递

适用移动语义可以将一个unique_lock赋值给另一个unique_lock,适用move实现。

void myThread1()
{
    unique_lock<mutex> myUnique (testMutex1,std::defer_lock);
    unique_lock<mutex>myUnique1(std::move(myUnique));//myUnique 则实效 myUnique1 相当于原来的myUnique
}

 std::lock_guard<std::mutex>(testMutex1,std::adopt_lock);  加上了std::adopt_lock就是lock_guard不会再次加锁了。

2.可多次lock的锁  std::recursive_mutex

工程中难免有多个地方要加锁,如果存在交叉调用则会出现异常,递归独占互斥量 std::recursive_mutex 可解决这个问题。

  • 就像互斥锁(mutex)一样,递归互斥锁(recursive_mutex)是可锁定的对象,但它允许同一线程获得对互斥锁对象的多级所有权(多次lock)。
  • 这允许从已经锁定它的线程锁定(或尝试锁定)互斥对象,从而获得对互斥对象的新所有权级别:互斥对象实际上将保持对该线程的锁定,直到调用其成员 unlock 的次数与此所有权级别的次数相同。

3.std::call_once

在多线程执行中,如果希望整个生命周期仅调用一次或者变量仅初始化一次,可以适用call_once.

C++11提供了一个函数 std::call_once(标记(once_flag), 函数名);

头文件在#include <mutex>

#include <iostream>
#include <thread>
#include <string>
#include <mutex>

using namespace std;

std::once_flag one_flag;

void myThread()//定义线程入口函数
{
  
	cout << "a value " << endl;
}


int main() {

	std::call_once(one_flag, myThread);
	std::call_once(one_flag, myThread);
	return 0;
}

看到这里可能会有些疑惑用互斥锁同样可以实现这个需求,显然互斥锁效率是要低的,因为每次使用这个线程都要上锁然后判断标记位 这样消耗的时间还会更长。

4.线程和协程

 

进程具有独立的内存地址空间。多个线程共用同一个地址空间。

  • 每个线程有自己的栈区,寄存器。
  • 多个线程共享代码段,堆区,全局数据区,打开的文件
  • 共享地址空间

 从操作系统层级上看,虚拟地址空间主要分为两个部分内核区和用户区。

内核区:

  • 内核空间为内核保留,不允许应用程序读写该区域的内容或直接调用内核代码定义的函数。
  • 内核总是驻留在内存中,是操作系统的一部分。
  • 系统中所有进程对应的虚拟地址空间的内核区都会映射到同一块物理内存上(系统内核只有一个)。

用户区:存储用户程序运行中用到的各种数据。

 每个进程的虚拟地址空间都是从 0 地址开始的,我们在程序中打印的变量地址也其在虚拟地址空间中的地址,程序是无法直接访问物理内存的。虚拟地址空间中用户区地址范围是 0~3G,里边分为多个区块:

  • 保留区: 位于虚拟地址空间的最底部,未赋予物理地址。任何对它的引用都是非法的,程序中的空指针(NULL)指向的就是这块内存地址。
  • .text段: 代码段也称正文段或文本段,通常用于存放程序的执行代码 (即 CPU 执行的机器指令),代码段一般情况下是只读的,这是对执行代码的一种保护机制。
  • .data段: 数据段通常用于存放程序中已初始化且初值不为 0 的全局变量和静态变量。数据段属于静态内存分配 (静态存储区),可读可写。
  • .bss段: 未初始化以及初始为 0 的全局变量和静态变量,操作系统会将这些未初始化变量初始化为 0
  • 堆(heap):用于存放进程运行时动态分配的内存。堆向高地址扩展 (即 “向上生长”),是不连续的内存区域。这是由于系统用链表来存储空闲内存地址,自然不连续,而链表从低地址向高地址遍历。
  • 内存映射区(mmap):作为内存映射区加载磁盘文件,或者加载程序运作过程中需要调用的动态库。
  • 栈(stack): 存储函数内部声明的非静态局部变量,函数参数,函数返回地址等信息,栈内存由编译器自动分配释放。栈和堆相反地址 “向下生长”,分配的内存是连续的。
  • 命令行参数:存储进程执行的时候传递给 main() 函数的参数,argc,argv []
  • 环境变量: 存储和进程相关的环境变量,比如:工作路径,进程所有者等信息。

线程的上下文切换比进程要快的多。切换之前保存当前任务状态。

线程的创建:

#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,创建失败返回对应的错误号

 

// pthread_create.c 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

// 子线程的处理代码
void* working(void* arg)
{
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<9; ++i)
    {
        printf("child == i: = %d\n", i);
    }
    return NULL;
}

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

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<3; ++i)
    {
        printf("i = %d\n", i);
    }
    
    // 休息, 休息一会儿...
    // sleep(1);
    
    return 0;
}

gcc pthread_create.c -lpthread

动态库名为 libpthread.so 需要使用的参数为 -l,根据规则掐头去尾最终形态应该写成:-lpthread(参数和参数值中间可以有空格)

线程退出

线程退出函数

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

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

线程 | 爱编程的大丙

与临界资源相关的上下文代码块成为临界区。

死锁:枷锁后忘记解锁。重复枷锁,造成死锁。

场景描述:
  1. 有两个共享资源:X, Y,X对应锁A, Y对应锁B
     - 线程A访问资源X, 加锁A
     - 线程B访问资源Y, 加锁B
  2. 线程A要访问资源Y, 线程B要访问资源X,因为资源X和Y已经被对应的锁锁住了,因此这个两个线程被阻塞
     - 线程A被锁B阻塞了, 无法打开A锁
     - 线程B被锁A阻塞了, 无法打开B锁

读写锁:读锁是共享的,写锁是独占的。

调用这个函数,如果读写锁是打开的,那么加锁成功;如果读写锁已经锁定了读操作,调用这个函数依然可以加锁成功,因为读锁是共享的;如果读写锁已经锁定了写操作,调用这个函数的线程会被阻塞

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

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

相关文章

在Linux中进行Jenkins部署(maven-3.9.1+jdk11)

Jenkins部署在公网IP为x.x.x.x的服务器上 maven-3.9.1要安装在jdk11环境中 环境准备 第一步&#xff0c;下载jdk-11.0.19_linux-x64_bin.tar.gz安装包。 登录地址&#xff1a;Java Downloads | Oracle 下载jdk-11.0.19_linux-x64_bin.tar.gz安装包&#xff0c;然后使用Win…

电子温湿度记录仪

电子温湿度记录仪&#xff1a;实时监测环境温度和湿度电子温湿度记录仪是一种用于实时监测环境温度和湿度的设备。它广泛应用于医疗、制药、食品加工、仓储、博物馆、实验室等领域&#xff0c;以确保环境温湿度处于合适的范围内&#xff0c;以保持物品和设备的稳定性和安全性。…

信号的产生——tripuls函数

信号的产生——tripuls函数, 功能&#xff1a;产生非周期三角波信号&#xff0c;其调用格式如下&#xff1a; &#xff08;1&#xff09;ytripuls(t)&#xff0c; &#xff08;2&#xff09;ytripuls(t,w)&#xff0c; &#xff08;3&#xff09;ytripuls(t,w,s)&#xff0…

Java多线程入门到精通学习大全?深入了解线程:生命周期、状态和优先级!(第二篇:线程的基础知识学习)

本文详细介绍了线程的基础知识&#xff0c;包括什么是线程、线程的生命周期、线程的状态和线程优先级等。在了解这些知识后&#xff0c;我们能够更好地掌握线程的使用方式&#xff0c;提高程序的并发性和效率。如果您对线程有更深入的问题&#xff0c;也欢迎向我们提问。 1. 什…

华为MPLS跨域——后门链路实验配置

目录 配置PE与CE设备对接命令&#xff08;通过OSPF对接&#xff09; 配置后门链路 可以使用任意方式来跑跨域MPLS&#xff08;A、B、C1、C2都可以&#xff09;&#xff0c;不过关于传递Vpnv4路由的配置此处不做介绍&#xff1b;此处只介绍关于PE和CE对接的配置和关于后门链路…

node.js的核心模块

node的核心模块由一些精简而高效的库组成 文章目录 全局对象全局对象和全局变量processcosole utilutils.inheritsutils.inspect 事件机制事件发射器error 事件继承EventEmitter 文件系统访问fs.readFile(filename,[encoding],[callback(err,data)])fs.readFileSync(filename,…

NSSCTF (2)

[GKCTF 2020]cve版签到 查看响应头发现有几个可能利用的信息 1.hint 里面的Flag in localhost 2.apache 2.4.38 3. php 7.3.15 我们发现第一个是提示 第二个apache版本没有什么漏洞 但是php 7.3.15 存在cve漏洞 这里题目规定 *.ctfhub.com 所以我们 要以这个结尾 在 p…

05-事务管理

概念&#xff1a; 事务是一组操作的集合&#xff0c;它是不可分割的工作单位&#xff0c;这些操作要么同时成功&#xff0c;要么同时失败 操作&#xff1a; 开启事务&#xff08;一组操作开始前&#xff0c;开启事务) : start transaction / begin ; 提交事务&#xff08;这组操…

【LeetCode】1000题挑战(230/1000)

1000题挑战 没有废话&#xff0c;直接开刷&#xff01; 目录 1000题挑战 没有废话&#xff0c;直接开刷&#xff01; 第一题&#xff1a;242. 有效的字母异位词 - 力扣&#xff08;Leetcode&#xff09; 题目接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; …

罗马数字转整数:探究古代数字编码的奥秘

本篇博客会讲解力扣中“13. 罗马数字转整数”这道题的解题思路。 大家先来审下题&#xff1a;题目链接。 题干如下&#xff1a; 有几个输出样例&#xff1a; 提示如下&#xff1a; 大家先思考一下&#xff0c;再来听我讲解。 本题的关键是&#xff1a;如何把罗马数字转换…

文末赠书3本 | 盼了一年的Core Java最新版卷Ⅱ,终于上市了

文章目录 盼了一年的Core Java最新版卷Ⅱ&#xff0c;终于上市了&#xff01;Core Java基于Java 17全面升级Core Java最新版卷Ⅱ现已上市卷Ⅰ、卷Ⅱ有何不同&#xff1f;如何阅读《Java核心技术》从未远离工业界的Java大神带你学50位行业专家、技术媒体赞誉推荐如何选择版本文末…

Python基础入门(2)—— 什么是控制语句、列表、元组和序列?

文章目录 01 | &#x1f684;控制语句02 | &#x1f685;列表03 | &#x1f688;元组04 | &#x1f69d;序列05 | &#x1f69e;习题 A bold attempt is half success. 勇敢的尝试是成功的一半。 前面学习了Python的基本原则、变量、字符串、运算符和数据类型等知识&#xff0c…

树形结构——堆的构建

在学习完我们的树形结构之后我们再来使用树形结构的思想构建一个堆。使用堆可以对我们的数据进行排序&#xff0c;并且堆排序的效率要远优于我们之前学习过的冒泡排序。下面我们来逐步实现堆这个数据结构。 在构建堆之前我们需要先介绍以下我们树形结构构建的两种形式&#xff…

掌握这些GitHub搜索技巧,你的开发效率将翻倍!

作为开发it行业一员&#xff0c;学习借鉴他人项目是很有必要的&#xff0c;所以我们一般都会从github或者 Gitee 上面去参考借鉴他人的项目来学习增加自己的项目经验 但是一般我还是在github上看项目比较多&#xff0c;毕竟人家实力项目量摆在那里 &#xff0c;但是国内访问gi…

Spring Cloud的那些组件和功能,get到了吗

Spring Cloud 是 Spring 技术栈生态很重要的一部分&#xff0c;面向大型网站服务端的开发和架构设计&#xff0c;它以 Spring/SpringBoot 为基础&#xff0c;提供的一系列组件规范和具体实现。 一、概述 Spring Cloud 提供了用于更快速构建分布式系统的基础规范&#xff08;例…

“深入”理解字节对齐

文章目录 前言思考查阅资料[Byte alignment and ordering](https://www.eventhelix.com/embedded/byte-alignment-and-ordering/)Byte Alignment RestrictionsWhy Restrict Byte Alignment?Compiler Byte PaddingUser Defined StructureActual Structure Definition Used By t…

使用SaleSmartly自动化流程的 5 个原因

想象一下&#xff0c;如果您可以采用智能数字解决方案来减轻团队和公司的手动和重复业务流程负担。它可以帮助您节省时间、提高公司的底线、消除冗余并增强数据管理。SaleSmartly&#xff08;ss客服&#xff09;就是这样。 通过利用自动化的力量&#xff0c;SaleSmartly&#x…

牛客网专项练习Pytnon分析库(一)

1.提取出a和b两个数组中的公共项,可以使用numpy库中的哪个函数&#xff08;A&#xff09;。 A.np.intersect1d(a,b) B.np.setdiff1d(a,b) C.np.where(a b) D.np.lexsort((a,b)) 解析&#xff1a; A选项&#xff0c;np.intersect1d用来获取数组a和数组b之间的公共项&…

MySQL遇到过死锁问题吗,你是如何解决的?

MySQL遇到过死锁问题吗&#xff0c;你是如何解决的&#xff1f; 问题解析 死锁&#xff0c;就是两个或者两个以上的线程在执行过程中&#xff0c;去争夺同一个共享资源导致互相等待的现象。 在没有外部干预的情况下&#xff0c;线程会一直处于阻塞状态&#xff0c;无法往下执行…

理解FPGA的基础知识——逻辑电路

FPGA (Field Programmable Gate Aray,现场可编程门阵列)是一种可通过重新编程来实现用户所需逻辑电路的半导体器件。为了便于大家理解FPGA的设计和结构&#xff0c;我们先来简要介绍一些逻辑电路的基础知识。 1.逻辑代数 逻辑代数中的变量称为逻辑变量&#xff0c;用大写字母表…