在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。
- 队列为空时,从队列中取数据将被阻塞,直到队列中有数据时被唤醒。
- 队列为满时,向队列中放入数据将被阻塞,直到队列中有数据取出被唤醒。
代码实现
下面是一个单生成单消费模型
LockGuard.hpp 文件 将加锁释放锁,交给一个对象处理,当对象创建加锁,对象销毁释放锁
#pragma once
#include <pthread.h>
class Mutex
{
public:
Mutex(pthread_mutex_t \*mutex):\_mutex(mutex)
{}
void Lock()
{
pthread\_mutex\_lock(_mutex);
}
void UnLock()
{
pthread\_mutex\_unlock(_mutex);
}
~Mutex()
{}
private:
pthread_mutex_t \*_mutex;
};
class LockGuard
{
public:
LockGuard(pthread_mutex_t \*mutex): \_lock(mutex)
{
_lock.Lock();
}
~LockGuard()
{
_lock.UnLock();
}
private:
Mutex _lock;
};
Blockqueue.hpp 文件
#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include "LockGuard.hpp"
using namespace std;
const int CAPACITY = 5;
template<class T>
class BlockQueue
{
public:
BlockQueue(int cap = CAPACITY):\_capacity(cap)
{
pthread\_mutex\_init(&_mutex, nullptr);
pthread\_cond\_init(&_p, nullptr);
pthread\_cond\_init(&_c, nullptr);
}
bool isFull()
{
return _bq.size() == _capacity;
}
void Push(const T &in)
{
LockGuard mutex(&_mutex);
//pthread\_mutex\_lock(&\_mutex);
while(isFull())
{
pthread\_cond\_wait(&_p, &_mutex);
}
_bq.push(in);
// 唤醒策略为 生产一个,消费一个
pthread\_cond\_signal(&_c);
//pthread\_mutex\_unlock(&\_mutex);
}
bool isEmpty()
{
return _bq.size() == 0;
}
void Pop(T \*out)
{
LockGuard mutex(&_mutex);
//pthread\_mutex\_lock(&\_mutex);
while(isEmpty())
{
pthread\_cond\_wait(&_c, &_mutex);
}
\*out = _bq.front();
_bq.pop();
// 唤醒策略为 消费一个,生产一个
pthread\_cond\_signal(&_p);
//pthread\_mutex\_unlock(&\_mutex);
}
~BlockQueue()
{
pthread\_mutex\_destroy(&_mutex);
pthread\_cond\_destroy(&_p);
pthread\_cond\_destroy(&_c);
}
private:
queue<T> _bq;
int _capacity;
pthread_mutex_t _mutex;
pthread_cond_t _p;
pthread_cond_t _c;
};
Task.hpp 文件
#pragma once
#include <string>
const char \*opers = "+-\*/%";
enum
{
ok = 0,
div_zero,
mod_zero
};
class Task
{
public:
Task()
{}
Task(int x, int y, char op) : \_data\_x(x), \_data\_y(y), \_oper(op)
{
_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;
}
}
break;
case '%':
{
if(_data_y == 0)
{
_code = mod_zero;
}
else
{
_result = _data_x % _data_y;
}
}
break;
default:
break;
}
}
void operator()()
{
Run();
}
std::string PrintTask()
{
std::string ret = std::to\_string(_data_x);
ret += _oper;
ret += std::to\_string(_data_y);
ret += "=?";
return ret;
}
std::string PrintResult()
{
std::string ret = std::to\_string(_data_x);
ret += _oper;
ret += std::to\_string(_data_y);
ret += "=";
if(_code == ok)
{
ret += std::to\_string(_result);
}
else
{
ret += "?";
}
ret += "[";
ret += std::to\_string(_code);
ret += "]";
return ret;
}
~Task()
{}
private:
int _data_x;
int _data_y;
char _oper;
int _result;
int _code; // 错误码
};
Main.cc 文件
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <ctime>
#include <string.h>
#include "BlockQueue.hpp"
#include "Task.hpp"
using namespace std;
void \*producer(void \*args)
{
BlockQueue<Task> \*bq = static\_cast<BlockQueue<Task> \*>(args);
// 产生任务
while (true)
{
int x = rand() % 10 + 1;
int y = rand() % 10 + 1;
char oper = opers[rand() % strlen(opers)];
Task task(x, y, oper);
cout << "producer: " << task.PrintTask() << endl;
bq->Push(task);
sleep(1);
}
return nullptr;
}
void \*consumer(void \*args)
{
// usleep(1000);
BlockQueue<Task> \*bq = static\_cast<BlockQueue<Task> \*>(args);
// 获取任务,处理任务
while (true)
{
if (bq->isFull())
{
Task task;
bq->Pop(&task);
task();
cout << "consumer: " << task.PrintResult() << endl;
//sleep(1);
}
}
}
int main()
{
srand(time(nullptr) ^ getpid());
BlockQueue<Task> bq;
pthread_t p;
pthread\_create(&p, nullptr, producer, (void \*)&bq);
pthread_t c;
pthread\_create(&c, nullptr, consumer, (void \*)&bq);
pthread\_join(p, nullptr);
pthread\_join(c, nullptr);
return 0;
}
那如何将这个单生产单消费该为多生产多消费呢?因为多生产多消费本质也是多个线程访问临界资源,那我们单生产和单消费不也是多个线程访问临界资源吗,所以我们不需要对BlockQueue.hpp文件进行修改,只需要在main函数中,创建多个生产者和消费者即可。
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <ctime>
#include <string.h>
#include "BlockQueue.hpp"
#include "Task.hpp"
using namespace std;
template <class T>
class ThreadData
{
public:
ThreadData(pthread_t tid, const string threadname, BlockQueue<T> \*bq)
: \_tid(tid), \_threadname(threadname), \_bq(bq)
{}
public:
pthread_t _tid;
string _threadname;
BlockQueue<T>\* _bq;
};
void \*producer(void \*args)
{
ThreadData<Task> \*data = static\_cast<ThreadData<Task> \*>(args);
// 产生任务
while (true)
{
int x = rand() % 10 + 1;
int y = rand() % 10 + 1;
char oper = opers[rand() % strlen(opers)];
Task task(x, y, oper);
cout << data->_tid << ", " << data->_threadname <<": " << task.PrintTask() << endl;
data->_bq->Push(task);
sleep(1);
}
return nullptr;
}
void \*consumer(void \*args)
{
// usleep(1000);
为了做好运维面试路上的助攻手,特整理了上百道 **【运维技术栈面试题集锦】** ,让你面试不慌心不跳,高薪offer怀里抱!
这次整理的面试题,**小到shell、MySQL,大到K8s等云原生技术栈,不仅适合运维新人入行面试需要,还适用于想提升进阶跳槽加薪的运维朋友。**
![](https://img-blog.csdnimg.cn/img_convert/987dc73e55e7036d8ada677ec6e3663f.png)
本份面试集锦涵盖了
* **174 道运维工程师面试题**
* **128道k8s面试题**
* **108道shell脚本面试题**
* **200道Linux面试题**
* **51道docker面试题**
* **35道Jenkis面试题**
* **78道MongoDB面试题**
* **17道ansible面试题**
* **60道dubbo面试题**
* **53道kafka面试**
* **18道mysql面试题**
* **40道nginx面试题**
* **77道redis面试题**
* **28道zookeeper**
**总计 1000+ 道面试题, 内容 又全含金量又高**
* **174道运维工程师面试题**
> 1、什么是运维?
> 2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?
> 3、现在给你三百台服务器,你怎么对他们进行管理?
> 4、简述raid0 raid1raid5二种工作模式的工作原理及特点
> 5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?
> 6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?
> 7、Tomcat和Resin有什么区别,工作中你怎么选择?
> 8、什么是中间件?什么是jdk?
> 9、讲述一下Tomcat8005、8009、8080三个端口的含义?
> 10、什么叫CDN?
> 11、什么叫网站灰度发布?
> 12、简述DNS进行域名解析的过程?
> 13、RabbitMQ是什么东西?
> 14、讲一下Keepalived的工作原理?
> 15、讲述一下LVS三种模式的工作过程?
> 16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?
> 17、如何重置mysql root密码?
详情docs.qq.com/doc/DSmdCdUNwcEJDTXFK
、简述raid0 raid1raid5二种工作模式的工作原理及特点
> 5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?
> 6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?
> 7、Tomcat和Resin有什么区别,工作中你怎么选择?
> 8、什么是中间件?什么是jdk?
> 9、讲述一下Tomcat8005、8009、8080三个端口的含义?
> 10、什么叫CDN?
> 11、什么叫网站灰度发布?
> 12、简述DNS进行域名解析的过程?
> 13、RabbitMQ是什么东西?
> 14、讲一下Keepalived的工作原理?
> 15、讲述一下LVS三种模式的工作过程?
> 16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?
> 17、如何重置mysql root密码?
详情docs.qq.com/doc/DSmdCdUNwcEJDTXFK