【linux】多线程(2)

文章目录

  • 线程的应用
    • 生产消费者模型
      • 自制锁
      • 生产消费队列
        • 成员参数
        • 生产函数
        • 消费函数
      • 任务处理方式
        • 主函数
  • POSIX信号量
    • sem_wait()
    • sem_post()
  • 线程池
    • 应用场景
    • 示例
  • 单例模式
    • 饿汉实现单例
  • 吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭.
    • 懒汉实现单例
  • 吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式.

线程的应用

生产消费者模型

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而
通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者
要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队
列就是用来给生产者和消费者解耦的。

在这里插入图片描述
其中生产则会与消费者相当于两个进程,进行生产与消费。

自制锁

通过封装线程锁,达到智能指针的效果,作用域结束,则自动释放锁,以防忘记释放锁,造成线程死锁!

#pragma  once
#include <pthread.h>
class Mutex
{
public:Mutex(pthread_mutex_t *lock): _lock(lock){}void Lock(){pthread_mutex_lock(_lock);}void Unlock(){pthread_mutex_unlock(_lock);}~Mutex(){}private:pthread_mutex_t *_lock;
};
class LockGuard
{
public:LockGuard(pthread_mutex_t *lock):_mutex(lock){_mutex.Lock();}~LockGuard(){_mutex.Unlock();} 
private:Mutex _mutex;
};

生产消费队列

成员参数
 std::queue<T> _q;int _capacity;pthread_mutex_t _mutex;pthread_cond_t _p_cond; // 给生产者的pthread_cond_t _c_cond; // 给消费者的
生产函数

判断队列是否是满的,如果满的那么则进行等待,

void Push(const T &in){LockGuard lockguard(&_mutex);//加锁。while (IsFull())//判断队列是否为满?{pthread_cond_wait(&_p_cond,&_mutex);    }_q.push(in);pthread_cond_signal(&_c_cond);}
消费函数

判断队列是否为空,若为空,则进行等待

void Pop(T *out){LockGuard lockguard(&_mutex);while(IsEmpty()){pthread_cond_wait(&_c_cond,&_mutex);}*out = _q.front();_q.pop();pthread_cond_signal(&_p_cond);}

整体:

#pragma  once#include <iostream>
#include <queue>
#include "LockGuard.hpp"
const int defaultcap = 5;
template <class T>
class BlockQueue
{
public:BlockQueue(int cap = defaultcap): _capacity(cap){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_p_cond, nullptr);pthread_cond_init(&_c_cond, nullptr);}bool IsFull(){return _q.size() == _capacity;}bool IsEmpty(){return _q.size() == 0;}void Push(const T &in){LockGuard lockguard(&_mutex);while (IsFull()){pthread_cond_wait(&_p_cond,&_mutex);    }_q.push(in);pthread_cond_signal(&_c_cond);}void Pop(T *out){LockGuard lockguard(&_mutex);while(IsEmpty()){pthread_cond_wait(&_c_cond,&_mutex);}*out = _q.front();_q.pop();pthread_cond_signal(&_p_cond);}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_p_cond);pthread_cond_destroy(&_c_cond);}
private:std::queue<T> _q;int _capacity;pthread_mutex_t _mutex;pthread_cond_t _p_cond; // 给生产者的pthread_cond_t _c_cond; // 给消费者的
};

任务处理方式

#pragma  once
#include <iostream>
#include <string>
#include <unistd.h>
const int defaultvalue = 0;
const std::string opers = "+-*/%)(&";
enum
{ok = 0,div_zero,mod_zero,unknow
};class Task
{
public:Task() {}Task(int x, int y, char op): data_x(x),data_y(y),oper(op),result(defaultvalue),code(ok){}void Run(){switch (oper){case '+':result = data_x + data_y;break;case '-':result = data_x - data_y;break;case '*':result = data_x * data_y;break;case '/':{if (data_y == 0)code = div_zero;elseresult = data_x / data_y;}case '%':{if (data_y == 0)code = mod_zero;elseresult = data_x % data_y;}break;default:code = unknow;break;}}void operator()(){Run();sleep(2);}std::string PrintTask(){std::string s;s = std::to_string(data_x);s += oper;s += std::to_string(data_y);s += "=?";return s;}std::string PrintResult(){std::string s;s = std::to_string(data_x);s += oper;s += std::to_string(data_y);s += std::to_string(result);s += '[';s += std::to_string(code);s += ']';return s;}~Task(){}private:int data_x;int data_y;char oper;int result;int code;
};
主函数
#include "BlockQueue.hpp"
#include "Task.hpp"
#include <pthread.h>
#include <ctime>
#include <sys/types.h>
#include <unistd.h>
class ThreadData
{
public:BlockQueue<Task> *bq;std::string name;
};
void *consumer(void *args)
{ThreadData *td = (ThreadData *)args;while (true){Task t;td->bq->Pop(&t);t();std::cout << "consumer data: " << t.PrintResult() << ", " << td->name << std::endl;}return nullptr;
}
void *productor(void *args)
{BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);while (true){int data1 = rand() % 10;usleep(rand() % 123);int data2 = rand() % 10;usleep(rand() % 123);char oper = opers[rand() % (opers.size())];Task t(data1, data2, oper);std::cout << "productor task: " << t.PrintTask() << std::endl;bq->Push(t);sleep(1);}return nullptr;
}int main()
{srand((uint16_t)time(nullptr) ^ getpid() ^ pthread_self());BlockQueue<Task> *bq = new BlockQueue<Task>();pthread_t c[3], p[2];ThreadData *td = new ThreadData();td->bq = bq;td->name = "thread_1";pthread_create(&c[0], nullptr, consumer, td);ThreadData *td1 = new ThreadData();td1->bq = bq;td1->name = "thread_2";pthread_create(&c[1], nullptr, consumer, td1);ThreadData *td2 = new ThreadData();td2->bq = bq;td2->name = "thread_3";pthread_create(&c[2], nullptr, consumer, td2);pthread_create(&p[0],nullptr,productor,bq);pthread_create(&p[1],nullptr,productor,bq);pthread_join(c[0], nullptr);pthread_join(c[1], nullptr);pthread_join(c[2], nullptr);pthread_join(p[0], nullptr);pthread_join(p[1], nullptr);return 0;
}

POSIX信号量

POSIX信号量(POSIX semaphores)是POSIX(Portable Operating System Interface)标准定义的一种同步原语,用于协调多线程或多进程之间的资源访问。它们提供了一种机制,允许一个或多个线程或进程等待某个条件成立,或者通知其他线程或进程某个条件已经成立。

sem_wait()

sem_wait 函数用于等待信号量 sem 变为非零值。具体来说,它执行以下操作:
检查信号量 sem 的当前值是否大于零。
如果大于零,则将其值减一,并立即返回。
如果等于零,则调用线程会被阻塞,直到信号量的值变为非零(通常是由另一个线程或进程通过 sem_post 操作增加信号量的值)。

sem_post()

sem_post 函数用于增加信号量 sem 的值。具体来说,它执行以下操作:
将信号量 sem 的值加一。
如果有线程在等待该信号量(即之前被 sem_wait 阻塞),则唤醒其中一个线程(通常是按照先进先出(FIFO)的顺序)。

拿上面的消费生产这模型举例:
将生产者的信号量设置为队列的大小,消费者的信号量设置为0.
当进行生产时,先进行sem_wait判断是否还有空位置进行生产,当生产完则将消费者的信号量进行增加,告诉消费者可以进行消费。
当进行消费时,则与之相反。

#pragma once
const int defaultsize = 5;template <class T>
class RingQueue
{
private:void P(sem_t &sem){sem_wait(&sem);}void V(sem_t &sem){sem_post(&sem);}public:RingQueue(int size = defaultsize): _ringqueue(size), _size(size), _p_step(0), _c_step(0){sem_init(&_space_sem, 0, size);sem_init(&_data_sem, 0, 0);pthread_mutex_init(&_p_mutex, nullptr);pthread_mutex_init(&_c_mutex, nullptr);}void Push(const T &in){// 生产P(_space_sem);{LockGuard lockGuard(&_p_mutex);_ringqueue[_p_step] = in;_p_step++;_p_step %= _size;}V(_data_sem);}void Pop(T *out){// 消费P(_data_sem);{LockGuard lockGuard(&_c_mutex);*out = _ringqueue[_c_step];_c_step++;_c_step %= _size;}V(_space_sem);}~RingQueue(){sem_destroy(&_space_sem);sem_destroy(&_data_sem);pthread_mutex_destroy(&_p_mutex);pthread_mutex_destroy(&_c_mutex);}private:std::vector<T> _ringqueue;int _size;int _p_step; // 生产者的生产位置int _c_step; // 消费位置sem_t _space_sem; // 生产者sem_t _data_sem;  // 消费者pthread_mutex_t _p_mutex;pthread_mutex_t _c_mutex;
};

线程池

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着
监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利
用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

应用场景

线程池的应用场景:

  1. 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个
    Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
  2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
  3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情
    况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误.

示例

  1. 创建固定数量线程池,循环从任务队列中获取任务对象
  2. 获取到任务对象后,执行任务对象中的任务接口

单例模式

某些类, 只应该具有一个对象(实例), 就称之为单例。

饿汉实现单例

吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭.

template <typename T>
class Singleton {
static T data;
public:
static T* GetInstance() {
return &data;
}
};

懒汉实现单例

吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式.

template <typename T>
class Singleton {
static T* inst;
public:
static T* GetInstance() {
if (inst == NULL) {
inst = new T();
}
return inst;
}
};

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

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

相关文章

visual studio code生成代码模板

编写需要生成代码片段的代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"wid…

【C++】二分查找算法:x的平方根

1.题目 2.算法思路 看到题目可能不容易想到二分查找。 这题考察我们对算法的熟练程度。 二分查找的特点&#xff1a;数组具有二段性(不一定有序)。 题目中没有数组&#xff0c;我们可以造一个从0到x的数组&#xff0c;然后利用二分查找找到对应的值即可。 3.代码 class S…

ionic关于@angular版本报错解决方案(有效)

最近学校要求使用ionicangular学习&#xff0c;但是出现下面问题&#xff0c;这里我就分享一个我亲测有效的解决方案&#xff0c;提供学习&#xff08;在VScode中&#xff09; npm error code ERESOLVE npm error ERESOLVE could not resolve npm error npm error While resol…

源码编译安装LAMP(安装apeche mysql php 论坛 网站 巨详细版)

目录 一.LAMP架构相关概述 1.各组件作用 Linux&#xff08;平台&#xff09; Apache&#xff08;前台&#xff09; MySQL&#xff08;后台&#xff09; PHP/Perl/Python&#xff08;中间连接&#xff09; 总结 二.编译安装Apache httpd服务 1.关闭防火墙&#xff0c;将…

解决SpringBoot中插入汉字变成?(一秒解决)

在这里url后面加一行配置即可&useUnicodetrue&characterEncodingUTF-8即可 解释 spring.datasource.url: 这里包含了数据库的URL&#xff0c;以及额外的参数如useUnicodetrue用于启用Unicode字符集支持&#xff0c;characterEncodingUTF-8用于指定字符编码为UTF-8&…

图像处理ASIC设计方法 笔记24 等价表和标记代换

&#xff08;一&#xff09;等价表的整理与压缩 1.1 等价关系的识别与追踪 在初步标记过程完成后&#xff0c;等价表的整理和压缩变得至关重要。这一阶段的首要任务是从等价表的地址1开始&#xff0c;对等价表进行逐个扫描。在扫描过程中&#xff0c;系统将检查每个临时标记是否…

链表类型的无界阻塞线程安全队列-ConcurrentLinkedQueue(FIFO)

ConcurrentLinkedQueue是非阻塞线程安全(volatile不能完全保证线程安全)的队列,适用于“高并发”的场景。是一个基于链表节点的无界线程安全队列,按照 FIFO(先进先出,尾先进头先出)原则对元素进行排序。队列元素中不可以放置null元素(内部实现的特殊节点除外)。 volati…

【rust工具链】

1 查看正在使用的工具链 命令&#xff1a;rustup show 结果显示&#xff1a; 从图中可以看到正在使用的是rustc 1.76.0版本&#xff0c;也可以看到已安装的所有版本的工具链 2 使用默认工具链 命令&#xff1a;rustup default 版本号 例如&#xff1a;rustup default 1.58…

Python语言绘制好看的小提琴图、箱形图、散点图、山脊图和柱状图等等

废话不多说&#xff0c;今天给大家分享一个&#xff0c;使用python绘制小提琴图、箱形图、散点图、山脊图和柱状图等等 图中的数据是随机生成的&#xff0c;图例&#xff0c;图注以及坐标题目各种信息&#xff0c;具体内容大家可以自己修改~ 效果图如下所示 &#x1f447;&a…

AIGC时代算法工程师的面试秘籍(2024.4.29-5.12第十三式) |【三年面试五年模拟】

写在前面 【三年面试五年模拟】旨在整理&挖掘AI算法工程师在实习/校招/社招时所需的干货知识点与面试方法&#xff0c;力求让读者在获得心仪offer的同时&#xff0c;增强技术基本面。也欢迎大家提出宝贵的优化建议&#xff0c;一起交流学习&#x1f4aa; 欢迎大家关注Rocky…

『ZJUBCA Weekly Feed 07』MEV | AO超并行计算机 | Eigen layer AVS生态

一文读懂MEV&#xff1a;区块链的黑暗森林法则 01 &#x1f4a1;TL;DR 这篇文章介绍了区块链中的最大可提取价值&#xff08;MEV&#xff09;概念&#xff0c;MEV 让矿工和验证者通过抢先交易、尾随交易和三明治攻击等手段获利&#xff0c;但也导致网络拥堵和交易费用增加。为了…

ThreadPoolExecutor线程池使用以及源码解析

文章目录 1. 引子2. 线程池源码分析2.1. 总览2.2. Executor2.3. ExecutorService2.4. AbstractExecutorService2.5. ThreadPoolExecutor构造函数核心参数阻塞队列拒绝策略核心属性线程池状态Worker 类execute() 方法addWorker() 方法runWorker() 方法getTask() 方法processWork…

奇舞周刊第529期:万字长文入门前端全球化

周五快乐&#xff08;图片由midjourney生成&#xff09; 奇舞推荐 ■ ■ ■ 万字长文入门前端全球化 目前国内企业正积极开拓国际市场&#xff0c;国际化已成为重要的发展方向&#xff0c;因此产品设计和开发更需考虑国际化。本文介绍了语言标识、文字阅读顺序等诸多知识。然后…

ubuntu安装samba实现共享文件windows可查看ubuntu中的文件

samba的作用&#xff1a;实现共享linux/ubuntu系统中的文件&#xff0c;在windows直接查看操作ubuntu/linux中的文件、文件夹 1、安装samba sudo apt-get install samba如果不能安装samba&#xff0c;则更新apt-get sudo apt-get upgrade sudo apt-get update sudo apt-get d…

LeetCode热题100—链表(一)

160.相交链表 题目 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#x…

【Unity2D 2022:Cinemachine】相机跟随与地图边界

一、导入Cinemachine工具包 1. 点击Window-Package Manager&#xff0c;进入包管理界面 2. 点击All&#xff0c;找到Cinemachine工具包&#xff0c;点击Install 二、相机跟随角色 1. 选中Main Camera&#xff0c;点击Component-Cinemachine-CinemachineBrain&#xff0c;新建…

Sping源码(八)—Spring事件驱动

观察者模式 在介绍Spring的事件驱动之前&#xff0c;先简单的介绍一下设计模式中的观察者模式。 在一个简单的观察者模式只需要观察者和被观察者两个元素。简单举个栗子&#xff1a; 以警察盯梢犯罪嫌疑人的栗子来说&#xff1a; 其中犯罪嫌疑人为被观察者元素而 警察和军人为…

【启程Golang之旅】基本变量与类型讲解

欢迎来到Golang的世界&#xff01;在当今快节奏的软件开发领域&#xff0c;选择一种高效、简洁的编程语言至关重要。而在这方面&#xff0c;Golang&#xff08;又称Go&#xff09;无疑是一个备受瞩目的选择。在本文中&#xff0c;带领您探索Golang的世界&#xff0c;一步步地了…

【JVM】内存区域划分 | 类加载的过程 | 双亲委派机制 | 垃圾回收机制

文章目录 JVM一、内存区域划分1.方法区&#xff08;1.7之前&#xff09;/ 元数据区&#xff08;1.8开始&#xff09;2.堆3.栈4.程序计数器常见面试题&#xff1a; 二、类加载的过程1.类加载的基本流程1.加载2.验证3.准备4.解析5.初始化 2.双亲委派模型类加载器找.class文件的过…

[JDK工具-5] jinfo jvm配置信息工具

文章目录 1. 介绍2. 打印所有的jvm标志信息 jinfo -flags pid3. 打印指定的jvm参数信息 jinfo -flag InitialHeapSize pid4. 启用或者禁用指定的jvm参数 jinfo -flags [|-]HeapDumpOnOutOfMemoryError pid5. 打印系统参数信息 jinfo -sysprops pid6. 打印以上所有配置信息 jinf…