面试之手撸安全队列
- [1. 代码](#1. 代码)
- [2. 测试](#2. 测试)
最近体验了一下面试,面试官要求用C++实现一个安全队列,要求是:
- 插入或读取数据时,有超时时间;
- 队列有长度限制,最大支持max_size;
- 该队列是线程安全的;
闲语不多说,直接上干货。
1. 代码
c++
#include<iostream>
#include<queue>
#include<mutex>
#include<condition_variable>
#include<chrono>
#include<thread>
#include<algorithm>
using namespace std;
template<typename T>
class FySafeQueue{
using Queue = std::queue<T>;
public:
explicit FySafeQueue(uint max_size, uint16_t timeout):
fy_max_size_(max_size),
fy_timeout_(timeout){
}
FySafeQueue(FySafeQueue& queue){
std::unique_lock<std::mutex> lg(this->fy_mtx);
this->fy_max_size_ = queue.fy_max_size_;
this->fy_timeout_ = queue.fy_timeout_;
this->fy_queue_ = queue.fy_queue_;
}
FySafeQueue(FySafeQueue&& queue){
std::unique_lock<std::mutex> lg(this->fy_mtx);
this->fy_max_size_ = queue.fy_max_size_;
this->fy_timeout_ = queue.fy_timeout_;
this->fy_queue_ = queue.fy_queue_;
}
FySafeQueue operator=(FySafeQueue& queue){
std::unique_lock<std::mutex> lg(this->fy_mtx);
this->fy_max_size_ = queue.fy_max_size_;
this->fy_timeout_ = queue.fy_timeout_;
this->fy_queue_ = queue.fy_queue_;
}
~FySafeQueue(){
this->fy_write_cv_.notify_all();
this->fy_read_cv_.notify_all();
}
bool pop(T &obj){
std::unique_lock<std::mutex> lg(this->fy_mtx);
auto ret = fy_read_cv_.wait_for(lg, std::chrono::milliseconds(this->fy_timeout_),
[this]{ return !this->fy_queue_.empty(); } );
if (ret){
obj = this->fy_queue_.front();
this->fy_queue_.pop();
this->fy_write_cv_.notify_one();
return true;
}
return false;
}
bool push(T &obj){
std::unique_lock<std::mutex> lg(this->fy_mtx);
auto ret = fy_write_cv_.wait_for(lg, std::chrono::milliseconds(this->fy_timeout_),
[this]{ return this->fy_queue_.size() < this->fy_max_size_; } );
if (ret){
this->fy_queue_.push(obj);
this->fy_read_cv_.notify_one();
return true;
}
return false;
}
size_t size(){
std::unique_lock<std::mutex> lg(this->fy_mtx);
return this->fy_queue_.size();
}
void clear(){
std::unique_lock<std::mutex> lg(this->fy_mtx);
while(!this->fy_queue_.empty()){
fy_queue_.pop();
}
}
private:
uint fy_max_size_;
uint16_t fy_timeout_; // unit: ms
Queue fy_queue_;
std::mutex fy_mtx;
std::condition_variable fy_read_cv_;
std::condition_variable fy_write_cv_;
};
FySafeQueue<int> glb_test_queue(2, 10);
void read_data();
void write_data(int data);
int main(int argc, char**argv){
std::cout << "*******************safety queue test********************" << std::endl;
std::cout << "1. signal thread test....................................." << std::endl;
int a = 16;
glb_test_queue.push(a);
std::cout << "glb_test_queue size: " << glb_test_queue.size() << std::endl;
int result;
if (glb_test_queue.pop(result)){
std::cout << "result: " << result << std::endl;
} else {
std::cout << "get data from glb_test_queue failed." << std::endl;
}
glb_test_queue.clear();
std::cout << "\n\n" << std::endl;
std::cout << "2. multi thread test....................................." << std::endl;
std::thread th1(write_data, 12);
std::thread th2(write_data, 11);
std::thread th3(write_data, 10);
std::this_thread::sleep_for(std::chrono::milliseconds(30));
std::cout << "queue size: " << glb_test_queue.size() << std::endl;
std::thread th4(read_data);
std::thread th5(read_data);
if (th1.joinable()) th1.join();
if (th2.joinable()) th2.join();
if (th3.joinable()) th3.join();
if (th4.joinable()) th4.join();
if (th5.joinable()) th5.join();
glb_test_queue.clear();
std::cout << "\n\n" << std::endl;
std::cout << "3. safety queue copy and operator= function test....................................." << std::endl;
write_data(22);
write_data(23);
write_data(24);
FySafeQueue<int> copy_queue(glb_test_queue);
if (copy_queue.pop(result)){
std::cout << "copy_queue, result: " << result << std::endl;
} else {
std::cout << "get data from copy_queue failed." << std::endl;
}
FySafeQueue<int> operator_queue = glb_test_queue;
if (operator_queue.pop(result)){
std::cout << "operator_queue, result: " << result << std::endl;
} else {
std::cout << "get data from operator_queue failed." << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "\n\n" << std::endl;
std::cout << "*******************safety queue test success********************" << std::endl;
}
void read_data(){
int result;
if (glb_test_queue.pop(result)){
std::cout << "thread id: " << std::this_thread::get_id() << ", result: " << result << std::endl;
} else {
std::cout << "get data from glb_test_queue failed." << std::endl;
}
}
void write_data(int data){
if (glb_test_queue.push(data) )
std::cout << "insert data to glb_test_queue success." << std::endl;
else
std::cout << "insert data to glb_test_queue failed, data: " << data << std::endl;
}
2. 测试
本次测试主要 分为三部分:
- 单线程测试,即主线程进行push&pop函数的测试;
- 多线程测试,多线程进行读和写的操作,以及最大长度测试;
- 测试拷贝构造和赋值函数。
代码已经在上文,这里直接上日志
*******************safety queue test********************
1. signal thread test.....................................
glb_test_queue size: 1
result: 16
2. multi thread test.....................................
insert data to glb_test_queue success.
insert data to glb_test_queue success.
insert data to glb_test_queue failed, data: 10
queue size: 2
thread id: 140324334204672, result: 12
thread id: 140324325811968, result: 11
3. safety queue copy and operator= function test.....................................
insert data to glb_test_queue success.
insert data to glb_test_queue success.
insert data to glb_test_queue failed, data: 24
copy_queue, result: 22
operator_queue, result: 22
*******************safety queue test success********************