【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;
            else
                result = data_x / data_y;
        }
        case '%':
        {
            if (data_y == 0)
                code = mod_zero;
            else
                result = 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;
}
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GMSL2硬件设计V1.1

一、说明 GMSL(Gigabit Multimedia Serial Links),中文名称为千兆多媒体串行链路,是Maxim公司(现属于ADI)推出的一种高速串行接口,通过同轴电缆或屏蔽双绞线(STP)传输高速串行数据,用于汽车摄像头和显示器应用。GMSL2就是指ADI专有的第二代千兆多媒体串行链路技术,传输…

重生之while在鸣潮学习HTML

个人主页&#xff1a;终端 今天是开荒的第五天&#xff0c;数据坞都刷了吗&#xff0c;没刷就过来学html&#xff01; 目录 JavaWeb学习路线 1.HTML入门 1.1什么是HTML 1.2HTML&CSS&JavaScript的作用 1.3什么是超文本 1.4什么是标记语言 1.5HTML基础结构 1.6HTML的…

通过acme.sh和cloudflare实现免费ssl证书自动签发

参考使用acme.sh通过cloudflare自动签发免费ssl证书 | LogDicthttps://www.logdict.com/archives/acme.shshi-yong-cloudflarezi-dong-qian-fa-mian-fei-sslzheng-shu

Jmeter-使用手册(_5.5版本)

JMeter是一个Java桌面应用程序&#xff0c;具有使用Swing图形API的图形界面。可以进行接口、性能等测试&#xff0c;也可以对任何数据库进行同样的测试&#xff0c;具有可移植性&#xff0c;可跨平台支持Windows&#xff0c;Linux&#xff0c;Mac上使用。 JMeter运行场景不仅可…

【openlayers系统学习】4.2Mapbox 样式渲染图层

二、Mapbox 样式渲染图层 显然我们目前的地图需要一些样式。 VectorTile​ 图层的样式与 Vector​ 图层的样式工作方式完全相同。那里描述的样式在这里也适用。 对于这样的地图&#xff0c;创建数据驱动的样式&#xff08;对矢量图层操作&#xff09;非常简单。但矢量切片也用…

AIGC 003-Controlnet升级你的SD让图像生成更加可控!

AIGC 003-Controlnet升级你的SD让图像生成更加可控&#xff01; 文章目录 0 论文工作1 论文方法2 效果 0 论文工作 ControlNet 论文 (Adding Conditional Control to Text-to-Image Diffusion Models) 提出了一种名为 ControlNet 的神经网络结构&#xff0c;旨在为大型文本到图…

趣店集团golang一面要个20K,Channel什么情况下会出现死锁,有遇到过吗?

结束后面试官加了VX&#xff0c;并询问方便二面的时间&#xff0c;一直还没回复&#xff0c;拖着拖着给忘啦... 面试题 1、自我介绍 2、你在团队里头负责哪一块&#xff0c;这个物流开放平台流量多大 3、为什么今年3月份被从物流开放团队转到了finance财务部门&#xff0c;感…

[SWPUCTF 2021 新生赛]pop

常见的魔术方法 魔术方法__construct() 类的构造函数&#xff0c;在对象实例化时调用 __destruct() 类的析构函数&#xff0c;在对象被销毁时被调用 __call() 在对象中调用一个不可访问的对象时被调用&#xff0c;比如一个对象被调用时&#xff0c;里面没有程序想调用的属性 …

​​​【收录 Hello 算法】10.4 哈希优化策略

目录 10.4 哈希优化策略 10.4.1 线性查找&#xff1a;以时间换空间 10.4.2 哈希查找&#xff1a;以空间换时间 10.4 哈希优化策略 在算法题中&#xff0c;我们常通过将线性查找替换为哈希查找来降低算法的时间复杂度。我们借助一个算法题来加深理解。 Question 给…

云上聚智共创未来 | 移动云的项目实战,10分钟让你获得高度可玩的个人博客网站

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 随着互联网的发展各种以前看起来离我们比较遥远的词越来越近了&#xff0c;比如 云服务、大数据、区块链、容器这些听起来…

04_前端三大件JS

文章目录 JavaScript1.JS的组成部分2.JS引入2.1 直接在head中通过一对script标签定义脚本代码2.2创建JS函数池文件&#xff0c;所有html文件共享调用 3.JS的数据类型和运算符4.分支结构5.循环结构6.JS函数的声明7.JS中自定义对象8.JS_JSON在客户端使用8.1JSON串格式8.2JSON在前…

#12松桑前端后花园周刊-SolidStart、Vercel融资、Angular18、Nextjs15RC、p5.js、ChromeDevTools引入AI

⚡️行业动态 SolidStart 1.0 元框架发布 Solidjs 核心团队发布其元框架 SolidStart 1.0 正式版&#xff0c;其特点如下&#xff1a;基于文件系统的路由&#xff1b;支持SSR、流式SSR、CSR、SSG渲染模式&#xff1b;通过代码分割、树摇和无用代码删除构建优化&#xff1b;基于…

LabVIEW超快激光微纳制造系统设计

LabVIEW超快激光微纳制造系统设计 在当前的制造行业中&#xff0c;精密加工技术的需求日益增长&#xff0c;尤其是在微纳尺度上。超快激光制造技术&#xff0c;以其独特的加工精度和加工效率&#xff0c;成为了精密加工领域的重要手段。然而&#xff0c;大多数超快激光制造系统…

05.爬虫---urllib与requests请求实战(GET)

05.urllib与Requests请求实战GET 1.Urllib模块2.Requests模块3.对比4.实战 1.Urllib模块 Urllib官方文档 https://docs.python.org/3/library/urllib.request.html urllib是Python的标准库&#xff0c;用于发送HTTP请求和处理响应。它提供了urlopen、Request等函数和类来与网络…

C++初阶学习第九弹——探索STL奥秘(四)——vector的深层挖掘和模拟实现

string&#xff08;上&#xff09;&#xff1a;C初阶学习第六弹——探索STL奥秘&#xff08;一&#xff09;——标准库中的string类-CSDN博客 string&#xff08;下&#xff09;&#xff1a;C初阶学习第七弹——探索STL奥秘&#xff08;二&#xff09;——string的模拟实现-CS…

GVM: Golang多版本管理利器

本文介绍了 Go Version Manager 的功能和使用方法&#xff0c;介绍了如何通过 GVM 在系统上安装和管理多个 Go 语言版本。原文: GVM: Go Version Manager, for Golang manage multiple versions Go 版本管理器&#xff08;GVM&#xff0c;Go Version Manager&#xff09;是一款…

X-CSV-Reader:一个使用Rust实现CSV命令行读取器

&#x1f388;效果演示 ⚡️快速上手 依赖导入&#xff1a; cargo add csv读取实现&#xff1a; use std::error::Error; use std::fs::File; use std::path::Path;fn read_csv<P: AsRef<Path>>(filename: P) -> Result<(), Box<dyn Error>> {le…

让大模型变得更聪明:人工智能的未来发展之路

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

slam14讲(第9,10讲 后端)

slam14讲&#xff08;第9&#xff0c;10讲 后端&#xff09; 后端分类基于滤波器的后端线性系统和卡尔曼滤波非线性系统和扩展卡尔曼滤波 BA优化H矩阵的稀疏性和边缘化H矩阵求解的总结 位姿图优化公式推导 基于滑动窗口的后端个人见解旧关键帧的边缘化 后端分类 基于滤波器的后…

融汇11款AI工具构建完美应用

本文将为您介绍25个开源项目&#xff0c;分为上下两篇以便您融汇它们来制作自己的AI应用。人工智能&#xff08;AI&#xff09;应用在近年来得到了长足的发展。从语音助手到软件开发&#xff0c;人工智能已在我们的生活中无处不在&#xff0c;并得到了广泛应用。 如您所见&…