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

Linux生产者消费模型

1.生产者消费者模型

1.1 为何要使用生产者消费者模型

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

1.2 生产者消费者模型优点

  • 解耦
  • 支持并发
  • 支持忙闲不均

 

 2.基于BlockingQueue的生产者消费者模型

2.1 BlockingQueue

     在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素;当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的,线程在对阻塞队列进程操作时会被阻塞)。

 2.2 C++ queue模拟阻塞队列的生产消费模型

#include <iostream>
#include <queue>
#include <stdlib.h>
#include <pthread.h>#define NUM 8class BlockQueue 
{
private:std::queue<int> q;int cap;pthread_mutex_t lock;pthread_cond_t full;pthread_cond_t empty;private:void LockQueue(){pthread_mutex_lock(&lock);}void UnLockQueue(){pthread_mutex_unlock(&lock);}void ProductWait(){pthread_cond_wait(&full, &lock);}void ConsumeWait(){pthread_cond_wait(&empty, &lock);}void NotifyProduct(){pthread_cond_signal(&full);}void NotifyConsume(){pthread_cond_signal(&empty);}bool IsEmpty(){return (q.size() == 0 ? true : false);}bool IsFull(){return (q.size() == cap ? true : false);}public:BlockQueue(int _cap = NUM) :cap(_cap){pthread_mutex_init(&lock, NULL);pthread_cond_init(&full, NULL);pthread_cond_init(&empty, NULL);}void PushData(const int& data){LockQueue();while (IsFull()){NotifyConsume();std::cout << "queue full, notify consume data, product stop." << std::endl;ProductWait();}q.push(data);// NotifyConsume();UnLockQueue();}void PopData(int& data){LockQueue();while (IsEmpty()){NotifyProduct();std::cout << "queue empty, notify product data, consume stop." << std::endl;ConsumeWait();}data = q.front();q.pop();// NotifyProduct();UnLockQueue();}~BlockQueue(){pthread_mutex_destroy(&lock);pthread_cond_destroy(&full);pthread_cond_destroy(&empty);}
};
pthread_t c, p;
pthread_create(&c, NULL, consumer, (void*)&bq);
pthread_create(&p, NULL, producter, (void*)&bq);pthread_join(c, NULL);
pthread_join(p, NULL);
return 0;
}void* consumer(void* arg)
{BlockQueue* bqp = (BlockQueue*)arg;int data;for (; ; ) {bqp->PopData(data);std::cout << "Consume data done : " << data << std::endl;}
}//more faster
void* producter(void* arg)
{BlockQueue* bqp = (BlockQueue*)arg;srand((unsigned long)time(NULL));for (; ; ) {int data = rand() % 1024;bqp->PushData(data);std::cout << "Prodoct data done: " << data << std::endl;// sleep(1);}
}
int main()
{BlockQueue bq;pthread_t c, p;pthread_create(&c, NULL, consumer, (void*)&bq);pthread_create(&p, NULL, producter, (void*)&bq);pthread_join(c, NULL);pthread_join(p, NULL);return 0;
}

3.POSIX信号量

      POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。

3.1 初始化信号量

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:pshared:0表示线程间共享,非零表示进程间共享value:信号量初始值

3.2 销毁信号量

int sem_destroy(sem_t *sem);

3.3 等待信号量

功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem); //P()

3.4 发布信号量

功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
int sem_post(sem_t *sem);//V()

4.基于环形队列的生产消费模型

  • 环形队列采用数组模拟,用模运算来模拟环状特性
  • 环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态

  • 但是我们现在有信号量这个计数器,就很简单的进行多线程间的同步过程
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>#define NUM 16class RingQueue 
{
private:std::vector<int> q;int cap;sem_t data_sem;sem_t space_sem;int consume_step;int product_step;public:RingQueue(int _cap = NUM) :q(_cap), cap(_cap){sem_init(&data_sem, 0, 0);sem_init(&space_sem, 0, cap);consume_step = 0;product_step = 0;}void PutData(const int& data){sem_wait(&space_sem); // Pq[consume_step] = data;consume_step++;consume_step %= cap;sem_post(&data_sem); //V}void GetData(int& data){sem_wait(&data_sem);data = q[product_step];product_step++;product_step %= cap;sem_post(&space_sem);}~RingQueue(){sem_destroy(&data_sem);sem_destroy(&space_sem);}
};
void* consumer(void* arg)
{RingQueue* rqp = (RingQueue*)arg;int data;for (; ; ) {rqp->GetData(data);std::cout << "Consume data done : " << data << std::endl;sleep(1);}
}//more faster
void* producter(void* arg)
{RingQueue* rqp = (RingQueue*)arg;srand((unsigned long)time(NULL));for (; ; ) {int data = rand() % 1024;rqp->PutData(data);std::cout << "Prodoct data done: " << data << std::endl;// sleep(1);}
}int main()
{RingQueue rq;pthread_t c, p;pthread_create(&c, NULL, consumer, (void*)&rq);pthread_create(&p, NULL, producter, (void*)&rq);pthread_join(c, NULL);pthread_join(p, NULL);
}

5.线程池

/*threadpool.h*/
/* 线程池:
* 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着
监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利
用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
* 线程池的应用场景:
* 1. 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技
术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个
Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
* 2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
* 3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情
况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,
出现错误.
* 线程池的种类:
* 线程池示例:
* 1. 创建固定数量线程池,循环从任务队列中获取任务对象,
* 2. 获取到任务对象后,执行任务对象中的任务接口
*//*threadpool.hpp*/
#ifndef __M_TP_H__
#define __M_TP_H__
#include <iostream>
#include <queue>
#include <pthread.h>#define MAX_THREAD 5typedef bool (*handler_t)(int);class ThreadTask
{
private:int _data;handler_t _handler;
public:ThreadTask() :_data(-1), _handler(NULL){}ThreadTask(int data, handler_t handler){_data = data;_handler = handler;}void SetTask(int data, handler_t handler){_data = data;_handler = handler;}void Run() {_handler(_data);}
};class ThreadPool
{
private:int _thread_max;int _thread_cur;bool _tp_quit;std::queue<ThreadTask*> _task_queue;pthread_mutex_t _lock;pthread_cond_t _cond;
private:void LockQueue() {pthread_mutex_lock(&_lock);}void UnLockQueue(){pthread_mutex_unlock(&_lock);}void WakeUpOne() {pthread_cond_signal(&_cond);}void WakeUpAll() {pthread_cond_broadcast(&_cond);}void ThreadQuit() {_thread_cur--;UnLockQueue();pthread_exit(NULL);}void ThreadWait(){if (_tp_quit){ThreadQuit();}pthread_cond_wait(&_cond, &_lock);}bool IsEmpty() {return _task_queue.empty();}static void* thr_start(void* arg){ThreadPool* tp = (ThreadPool*)arg;while (1) {tp->LockQueue();while (tp->IsEmpty()){tp->ThreadWait();}ThreadTask* tt;tp->PopTask(&tt);tp->UnLockQueue();tt->Run();delete tt;}return NULL;}
public:ThreadPool(int max = MAX_THREAD) :_thread_max(max), _thread_cur(max),_tp_quit(false) {pthread_mutex_init(&_lock, NULL);pthread_cond_init(&_cond, NULL);}~ThreadPool() {pthread_mutex_destroy(&_lock);pthread_cond_destroy(&_cond);}bool PoolInit() {pthread_t tid;for (int i = 0; i < _thread_max; i++) {int ret = pthread_create(&tid, NULL, thr_start, this);if (ret != 0) {std::cout << "create pool thread error\n";return false;}}return true;}bool PushTask(ThreadTask* tt){LockQueue();if (_tp_quit) {UnLockQueue();return false;}_task_queue.push(tt);WakeUpOne();UnLockQueue();return true;}bool PopTask(ThreadTask** tt) {*tt = _task_queue.front();_task_queue.pop();return true;}bool PoolQuit(){LockQueue();_tp_quit = true;UnLockQueue();while (_thread_cur > 0) {WakeUpAll();usleep(1000);}return true;}
};
#endif/*main.cpp*/
bool handler(int data)
{srand(time(NULL));int n = rand() % 5;printf("Thread: %p Run Tast: %d--sleep %d sec\n", pthread_self(), data, n);sleep(n);return true;
}int main()
{int i;ThreadPool pool;pool.PoolInit();for (i = 0; i < 10; i++){ThreadTask* tt = new ThreadTask(i, handler);pool.PushTask(tt);}pool.PoolQuit();return 0;
}
g++ -std=c++0x test.cpp -o test -pthread -lrt

6.线程安全的单例模式

6.1 什么是单例模式

     单例模式是一种 "经典的, 常用的, 常考的" 设计模式。

6.2 什么是设计模式

     IT行业这么火, 涌入的人很多. 俗话说林子大了啥鸟都有. 大佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖大佬的后腿, 于是大佬们针对一些经典的常见的场景, 给定了一些对应的解决方案, 这个就是 设计模式。

6.3 单例模式的特点

      某些类, 只应该具有一个对象(实例), 就称之为单例。例如一个男人只能有一个媳妇。在很多服务器开发场景中, 经常需要让服务器加载很多的数据 (上百G) 到内存中。此时往往要用一个单例的类来管理这些数据。

6.4 饿汉实现方式和懒汉实现方式

吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭.
吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式.
       懒汉方式最核心的思想是 "延时加载". 从而能够优化服务器的启动速度。

6.5 饿汉方式实现单例模式

template <typename T>
class Singleton 
{static T data;
public:static T* GetInstance(){return &data;}
};
    只要通过 Singleton 这个包装类来使用 T 对象, 则一个进程中只有一个T对象的实例。

6.6 懒汉方式实现单例模式

template <typename T>
class Singleton
{static T* inst;
public:static T* GetInstance() {if (inst == NULL){inst = new T();}return inst;}
};
    存在一个严重的问题, 线程不安全。第一次调用 GetInstance 的时候, 如果两个线程同时调用, 可能会创建出两份T对象的实例。但是后续再次调用, 就没有问题了。

6.7 懒汉方式实现单例模式(线程安全版本) 

// 懒汉模式, 线程安全
template <typename T>
class Singleton 
{volatile static T* inst; // 需要设置 volatile 关键字, 否则可能被编译器优化.static std::mutex lock;
public:static T* GetInstance() {if (inst == NULL) { // 双重判定空指针, 降低锁冲突的概率, 提高性能.lock.lock(); // 使用互斥锁, 保证多线程情况下也只调用一次 new.if (inst == NULL) {inst = new T();}lock.unlock();}return inst;}
};
     注意事项:
  1. 加锁解锁的位置
  2. 双重 if 判定, 避免不必要的锁竞争
  3. volatile关键字防止过度优化

7. STL,智能指针和线程安全

7.1 STL中的容器是否是线程安全的?

    不是. 原因是, STL 的设计初衷是将性能挖掘到极致, 而一旦涉及到加锁保证线程安全, 会对性能造成巨大的影响. 而且对于不同的容器, 加锁方式的不同, 性能可能也不同(例如hash表的锁表和锁桶). 因此 STL 默认不是线程安全. 如果需要在多线程环境下使用, 往往需要调用者自行保证线程安全.

 7.2 智能指针是否是线程安全的?

     对于 unique_ptr, 由于只是在当前代码块范围内生效, 因此不涉及线程安全问题. 对于shared_ptr, 多个对象需要共用一个引用计数变量, 所以会存在线程安全问题. 但是标准库实现的时候考虑到了这
个问题, 基于原子操作(CAS)的方式保证 shared_ptr 能够高效, 原子的操作引用计数.

 7.3 其他常见的各种锁

  • 悲观锁:在每次取数据时,总是担心数据会被其他线程修改,所以会在取数据前先加锁(读锁,写锁,行锁等),当其他线程想要访问数据时,被阻塞挂起。
  • 乐观锁:每次取数据时候,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要采用两种方式:版本号机制和CAS操作。
  • CAS操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不断重试。
  • 自旋锁,公平锁,非公平锁?

 8.  读者写者问题

8.1 读写锁

     在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少。相比较改写,它们读的机会反而高的多。通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长。给这种代码段加锁,会极大地降低我们程序的效率。那么有没有一种方法,可以专门处理这种多读少写的情况呢? 有,那就是读写锁。

  •  注意:写独占,读共享,读锁优先级高

8.2  读写锁接口

8.2.1 设置读写优先

int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
/*
pref 共有 3 种选择PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况
PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和
PTHREAD_RWLOCK_PREFER_READER_NP 一致PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁
*/

8.2.2 初始化

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t
*restrict attr);

8.2.3 销毁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

8.2.4 加锁和解锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

8.2.5 读写锁案例

#include <vector>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <pthread.h>volatile int ticket = 1000;
pthread_rwlock_t rwlock;void* reader(void* arg)
{char* id = (char*)arg;while (1){pthread_rwlock_rdlock(&rwlock);if (ticket <= 0) {pthread_rwlock_unlock(&rwlock);break;}printf("%s: %d\n", id, ticket);pthread_rwlock_unlock(&rwlock);usleep(1);}return nullptr;
}void* writer(void* arg)
{char* id = (char*)arg;while (1) {pthread_rwlock_wrlock(&rwlock);if (ticket <= 0) {pthread_rwlock_unlock(&rwlock);break;}printf("%s: %d\n", id, --ticket);pthread_rwlock_unlock(&rwlock);usleep(1);}return nullptr;
}struct ThreadAttr
{pthread_t tid;std::string id;
};std::string create_reader_id(std::size_t i)
{// 利用 ostringstream 进行 string 拼接std::ostringstream oss("thread reader ", std::ios_base::ate);oss << i;return oss.str();
}std::string create_writer_id(std::size_t i)
{// 利用 ostringstream 进行 string 拼接std::ostringstream oss("thread writer ", std::ios_base::ate);oss << i;return oss.str();
}void init_readers(std::vector<ThreadAttr>& vec)
{for (std::size_t i = 0; i < vec.size(); ++i) {vec[i].id = create_reader_id(i);pthread_create(&vec[i].tid, nullptr, reader, (void*)vec[i].id.c_str());}
}void init_writers(std::vector<ThreadAttr>& vec)
{for (std::size_t i = 0; i < vec.size(); ++i) {vec[i].id = create_writer_id(i);pthread_create(&vec[i].tid, nullptr, writer, (void*)vec[i].id.c_str());}
}void join_threads(std::vector<ThreadAttr> const& vec)
{// 我们按创建的 逆序 来进行线程的回收for (std::vector<ThreadAttr>::const_reverse_iterator it = vec.rbegin(); it !=vec.rend(); ++it) {pthread_t const& tid = it->tid;pthread_join(tid, nullptr);}
}void init_rwlock()
{
#if 0 // 写优先pthread_rwlockattr_t attr;pthread_rwlockattr_init(&attr);pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);pthread_rwlock_init(&rwlock, &attr);pthread_rwlockattr_destroy(&attr);
#else // 读优先,会造成写饥饿pthread_rwlock_init(&rwlock, nullptr);
#endif
}int main()
{// 测试效果不明显的情况下,可以加大 reader_nr// 但也不能太大,超过一定阈值后系统就调度不了主线程了const std::size_t reader_nr = 1000;const std::size_t writer_nr = 2;std::vector<ThreadAttr> readers(reader_nr);std::vector<ThreadAttr> writers(writer_nr);init_rwlock();init_readers(readers);init_writers(writers);join_threads(writers);join_threads(readers);pthread_rwlock_destroy(&rwlock);
}
main: main.cppg++ -std=c++11 -Wall -Werror $^ -o $@ -lpthread

相关文章:

Linux生产者消费模型

1.生产者消费者模型 1.1 为何要使用生产者消费者模型 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯&#xff0c;而通过阻塞队列来进行通讯&#xff0c;所以生产者生产完数据之后不用等待消费者处理&#xff0c;直接…...

动态网站开发讲课笔记01:网页开发基础

文章目录零、本讲学习目标一、HTML基础&#xff08;一&#xff09;HTML简介1、HTML2、HTML语言的基本格式3、<!DOCTYPE>声明4、html标签5、head标签6、body标签7、编写第一个网页8、关于编写HTML文件的工具9、HTML标签概述&#xff08;1&#xff09;单标签&#xff08;2&…...

互联网新时代要到来了(三)什么是ChatGPT?

什么是ChatGPT? tips&#xff1a;资料来自百度百科、openAi、CSDN博主「琦在江湖飘」、Info写作社区、CSDN博主「夕小瑶」等网页资料。 1.什么是ChatGPT&#xff1f; ChatGPT&#xff08;全名&#xff1a;Chat Generative Pre-trained Transformer&#xff09;&#xff0c;…...

华为OD机试 - 环中最长子串(Python)

环中最长子串 题目 给你一个字符串s,首尾相连成一个环形, 请你在环中找出o字符出现了偶数次最长子字符串的长度. 备注: 1 <= s.lenth <= 5x10^5 s只包含小写英文字母 输入 输入是一个小写字母组成的字符串 输出描述 输出是一个整数 示例一 输入 alolobo输出 6说…...

安全—08day

ApabilitiesapabilitiesLinux Capabilities线程的 capabilitiesPermitted 允许Effective 有效InheritableBoundingAmbient文件的 capabilitiesPermittedInheritableEffective运行 execve() 后 capabilities 的变化案例分析方法一、依次执行如下命令方法二、iptables端口转发方案…...

【看表情包学Linux】进程地址空间 | 区域和页表 | 虚拟地址空间 | 初识写时拷贝

&#x1f923; 爆笑教程 &#x1f449; 《看表情包学Linux》&#x1f448; 猛戳订阅 &#x1f525; &#x1f4ad; 写在前面&#xff1a;本章核心主题为 "进程地址空间"&#xff0c;会通过验证 Linux 进程的地址空间来开头&#xff0c;抛出 "同一个值能有不同内…...

响应式编程(Reactive Programming)介绍

什么是响应式编程? 在互联网上有着一大堆糟糕的解释与定义。Wikipedia 一如既往的空泛与理论化。Stackoverflow 的权威答案明显不适合初学者。Reactive Manifesto 看起来是你展示给你公司的项目经理或者老板们看的东西。微软的 Rx terminology"Rx Observables LINQ S…...

你不知道的美化列表的两种方案-<ul/><ol/>

大家好,我是半夏👴,一个励志更文1000篇沙雕程序员.如果喜欢我的文章,可以关注➕ 点赞 一起学习交流前端,成为更优秀的工程师~ CSS为什么这么难学?一定是你方法不对!!! 只要一杯奶茶,CSS任你学。学透CSS,拒绝切图仔!!! 学透CSS传送门 文章目录 学透CSS传送门前言li…...

2023年浙江理工大学MBA招生考试初试成绩查询及复查的通知

根据往年的情况&#xff0c;2023浙江理工大学MBA考试初试成绩可能将于2月21日下午两点公布&#xff0c;为了广大考生可以及时查询到自己的分数&#xff0c;杭州达立易考教育为大家汇总了信息。 一、成绩查询考生可登录中国研究生招生信息网“全国硕士研究生招生考试初试成绩查询…...

SVNH数据(.mat格式)转为图像(.png)matlab代码

一、获取SVNH数据数据集集地址-http://ufldl.stanford.edu/housenumbers/提供两种格式的数据&#xff1a;1.Format 1&#xff0c;图像形式&#xff0c;压缩包2.Format 2&#xff0c; .mat格式的数据10 classes, 1 for each digit. Digit 1 has label 1, 9 has label 9 and 0 ha…...

【总结】vim教程与详细命令总结,该来的躲不掉啊晕

B站|公众号&#xff1a;啥都会一点的研究生 目录写在前面vim的工作模式普通模式编辑模式命令模式命令大全&#xff0c;最详细&#xff08;建议收藏&#xff09;光标的移动插入模式 - 插入/追加文本编辑文本选择文本&#xff08;可视化模式&#xff09;可视化模式命令剪切, 复制…...

git基础使用

Git安装 去安装>> 正式开始 进入要管理的目录&#xff0c;执行命令 git init 查看管理目录下的状态 git status 注&#xff1a;新增文件和修改过后的文件都是红色 管理指定文件&#xff08;红变绿&#xff09; 指定文件&#xff1a;git add 文件名 当前目录下所有&…...

基于 RANSAC 的地面分割与聚类算法

文章目录 前言 一、算法原理 参考文献 二、代码实现 1.头文件 2.源文件...

JVM内存模型深度剖析与优化

1. Java语言的跨平台特性 2. JVM整体结构及内存模型 堆存放着对象信息每个线程都会分配一块属于自己的内存空间&#xff08;栈空间&#xff09; 每个方法都会分配一块内存空间&#xff08;栈桢&#xff09;&#xff0c;上图 compute()方法 和 main()方法 都会分配到各自的栈桢空…...

软件性能测试定义中文

From Wiki软件性能测试在软件质量保证中&#xff0c;性能测试通常是一种测试实践&#xff0c;用于确定系统在特定工作负载下的响应能力和稳定性方面的表现。它还可以用于调查、测量、验证或验证系统的其他质量 属性&#xff0c;例如可扩展性、可靠性和资源使用。性能测试是性能…...

2023情人节正经性生活调研报告

省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2023年1月份热门报告合集ChatGPT的发展历程、原理、技术架构及未来方向2023年&#xff0c;如何科学制定年度规划&#xff1f;《底层逻辑》高清配图今天给大家带来丁香医生最新…...

22- 隐马尔科夫HMM (NLP自然语言算法) (算法)

HMM模型 : from hmmlearn.hmm import GaussianHMM model GaussianHMM(n_components3,n_iter100000, covariance_type diag) model.fit(X) 1、马尔科夫链 有向图模型&#xff08;贝叶斯网络&#xff09;&#xff1a;用有向图表示变量间的依赖关系&#xff1b; 无向图模型&…...

gRPC是什么,怎么用

RPC是什么 RPC是指远程过程调用&#xff0c;也就是说两台服务器A&#xff0c;B&#xff0c;一个应用部署在A服务器上&#xff0c;想要调用B服务器上应用提供的函数/方法&#xff0c;由于不在一个内存空间&#xff0c;不能直接调用&#xff0c;需要通过网络来表达调用的语义和传…...

linux基本功系列之fdisk命令实战

文章目录前言一. fdisk命令介绍二. 语法格式及常用选项三. 参考案例3.1 列出每个分区的大小3.2 分区操作3.2.1 添加硬盘3.2.2 开启虚拟机并分区3.3.3 分区完成后进行格式化挂载四 . 设置分区自动挂载前言 大家好&#xff0c;又见面了&#xff0c;我是沐风晓月&#xff0c;本文…...

Mysql UDF提权复现Raven2

Raven2通关过程 主要通过Raven2靶机进行复现Mysql UDF提权&#xff0c;以下为通关过程。 靶机镜像&#xff1a;https://www.vulnhub.com/entry/raven-2,269/ 信息收集 拿到靶机ip&#xff1a;192.168.112.129 nmap -sP 192.168.112.0/24探测开放端口&#xff0c;nmap用烂了…...

枚举类(enum)

定义&#xff1a;在某些情况下&#xff0c;一个类的实例对象是有限且固定的&#xff0c;可将该类称为“枚举类”。枚举类是JDK 1.5 之后提出来的。例如&#xff1a;四季只有春夏秋冬4个季节&#xff0c;性别只有男女2个&#xff0c;故四季类和性别类均可称为“枚举类”。 在自…...

腾讯云架构师亲码“redis深度笔记”,从入门到精通,面面俱到

前言 作为这个时代码代码的秃头人员&#xff0c;对Redis肯定是不陌生的&#xff0c;如果连Redis都没用过&#xff0c;还真不好意思出去面试&#xff0c;指不定被面试官吊打多少次。 毕竟现在互联网公司和一些创业公司都要用到Redis&#xff0c;像亚马逊、谷歌、阿里、腾讯都要…...

萌新应该如何开始学习走向自动化测试高薪岗位?

对于测试人员来说&#xff0c;不管进行功能测试还是自动化测试&#xff0c;还是性能测试&#xff0c;都是需要编写测试用例&#xff0c;所以我们必须先要了解清楚手工测试用例与自动化测试用例的一些特点&#xff0c;才能更好的开展自动化测试工作。1.1手工测试用例和自动化测试…...

-bash: pip: command not found

背景 这个错误的原因就是&#xff0c;我们的服务器上没有安装pip&#xff0c;装上就可以了&#xff0c;下面我们看一下centos中的解决方案 下载 wget https://bootstrap.pypa.io/get-pip.py 下载完成后如下图&#xff1a; 安装 安装的时候首先需要看一下自己的python是什…...

使用HTTP隧道代理,请求超过频率要怎么办?

在网上&#xff0c;经常会看到有人说使用隧道代理经常遇到429错误&#xff08;请求超过频率&#xff09;&#xff0c;我们要如何解决这一问题呢&#xff1f;通常情况&#xff0c;优质的HTTP代理厂商隧道代理服务器采用的是高性能主机构建的动态IP代理服务器&#xff0c;是可以支…...

paddle 49 ODConv的可部署调整

ODConv是一种适用于轻量化模型的conv结构,可以在较少的参数下训练出多参数模型才能达到的精度,在相同的flop下可以稳定的涨2-3%个点。但是在paddle下部署ODConv动态卷积模型时会报出各种异常,导致模型无法转静态图或onnx格式(可能在pytorch下也是无法转换的)。为此研究ODC…...

C++ STL 学习之【string】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f38a;每篇一句&#xff1a; 图片来源 The key is to keep company only with people who uplift you, whose presence calls forth your best. 关键是只与那些提升你的人在一起&#xff0c…...

使用开源 MaxKey 与 APISIX 网关保护你的 API

1. Apache APISIX介绍 Apache APISIX 是 Apache 软件基金会下的云原生 API 网关&#xff0c;它兼具动态、实时、高性能等特点&#xff0c;提供了负载均衡、动态上游、灰度发布&#xff08;金丝雀发布&#xff09;、服务熔断、身份认证、可观测性等丰富的流量管理功能。我们可以…...

Linux之Xshell工具使用

shell简介Xshell是一个远程工具&#xff0c;可以远程连接linux系统 &#xff0c;SSH&#xff0c;远程管理 Xshell来远程访问Linux系统的终端 。shell的英文含义是“壳”&#xff1b;它是相对于内核来说的&#xff0c;因为它是建立在内核的基础上&#xff0c;面向于用户的一种表…...

【数据结构与算法】时间复杂度与空间复杂度

目录 一.前言 二.时间复杂度 1.概念 二.大O的渐进表示法 概念&#xff1a; 总结&#xff1a; 三.常见时间复杂度计算举例 例1 例2 例3 例4 例5.计算冒泡排序的时间复杂度 例6.二分算法的时间复杂度 例7.阶乘递归Fac的时间复杂度 例8.斐波那契递归的时间复杂度 …...

高端品牌衣服排行榜前十名/免费的seo网站

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2021年车工&#xff08;初级&#xff09;考试题及车工&#xff08;初级&#xff09;复审模拟考试&#xff0c;包含车工&#xff08;初级&#xff09;考试题答案和解析及车工&#xff08;初级&#xff09;复审模拟考试…...

怎么创建一个博客网站/营销型网站建设总结

作者 | 利开园责编 | Carol封图 | CSDN 下载自视觉中国很多开发者都遇到类似这样的经历&#xff1a;一个产品功能开发测试都正常&#xff0c;发布上线后也正常&#xff0c;但是过一段后&#xff0c;如果有个活动或流量一大程序就突然卡了&#xff0c;也有可能流量正常也没搞活动…...

微信朋友圈广告推广代理/搜索引擎优化结果

前言 突然想整整VLC-Android&#xff0c;然后就下一个玩玩看&#xff0c;这里记录点遇到的问题。 声明欢迎转载&#xff0c;但请保留文章原始出处:) 博客园&#xff1a;http://www.cnblogs.com农民伯伯&#xff1a; http://over140.cnblogs.com 正文 本文vlc-android的版本为&…...

vue做单页面网站/网络平台推广

前几天的3.15晚会上曝光了利用智能机器人&#xff0c;一天打4万个骚扰电话&#xff0c;从而赚取利润的黑色产业链。 阿里的工程师恼了&#xff0c;技术是用来让人们生活变美好的&#xff0c;不是被利用来走向阴暗的。 机器人的问题交给机器人&#xff01; 工程师们用业余时间开…...

wordpress 模板 中文/网址导航推广

本篇文章是一篇关于工程图标的帖子 本例为iPhone iphone 程序logo像素为57X57 或114X114&#xff08;其他不可以&#xff09;而且格式为png 1、将准备好的图片添加到工程目录然后在工程中选择如下图 2、向下找到appicon 每日一道理 时间好比一条小溪&#xff0c;它能招引我们奔…...

个人做金融网站能赚钱吗/珠海关键词优化软件

本文适合初学者阅读 5.3 切片切片本身并非动态数组或数组指针. 它内部通过指针引用底屋数组, 设定相关属性将数据读写操作限定在指定区域内.切片本身是只只读对象, 其工作机制类似数据指针的一种包装.可基于数组或数组指针创建切片, 以开始和结束索引位置确定所引用的数组片段…...