執行緒安全佇列(使用互斥鎖進行實現)
沒有設定佇列上限的執行緒安全佇列
只需要採取一個std::condition_variable變數,用於處理佇列為空的情況
以下是示例程式碼,涉及了std::mutex和std::condition_variable、std::unique_lock、std::lockguard等多執行緒互動的類。
測試方式採取的是3個生成者和一個消費者,消費者監測生產者push的資料是否有異常,同時拿一個執行緒在十秒後呼叫Finish()函式。
示例1:沒有設定佇列上限的執行緒安全佇列(包含測試程式碼)
#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <atomic>
#include <functional>
#include <string>
#include <chrono>
#include <map>
template <typename T>
class ThreadSafetyQueue
{
public:
ThreadSafetyQueue() : finish_(false) {}
bool push(T &data)
{
std::unique_lock<std::mutex> lock(mtx_);
if(finish_)
{
std::cout << "queue finish" << std::endl;
return false;
}
data_queue_.push(data);
lock.unlock(); // 注意:解鎖必須在通知之前
cv_.notify_one();
return true;
}
bool pop(T &data)
{
std::unique_lock<std::mutex> lock(mtx_);
while(!finish_ && data_queue_.empty()) // 防止假醒
{
cv_.wait(lock);
}
if(finish_)
{
std::cout << "queue finish" << std::endl;
return false;
}
data = data_queue_.front();
data_queue_.pop();
return true;
}
bool empty()
{
std::unique_lock<std::mutex> lock(mtx_);
if(data_queue_.empty())
{
return true;
}
return false;
}
int size()
{
std::unique_lock<std::mutex> lock(mtx_);
return data_queue_.size();
}
void Finish()
{
finish_ = true;
cv_.notify_all();
}
private:
std::mutex mtx_;
std::queue<T> data_queue_;
std::condition_variable cv_;
std::atomic_bool finish_;
};
struct test_data
{
int num;
std::string str;
};
int main()
{
ThreadSafetyQueue<test_data> queue;
std::function<void(std::string)> product_func = ([&queue](std::string name){
for(int i = 0; i < 1000000; ++i)
{
test_data td{i, name};
queue.push(td);
}
});
std::function<void(void)> consumer_func = ([&queue](){
std::map<std::string, int> test_mp;
while(1)
{
test_data data;
if(queue.pop(data))
{
if(test_mp.find(data.str) == test_mp.end())
{
test_mp[data.str] = 0;
std::cout << "add consumer name is: " << data.str << std::endl;
}
if(test_mp[data.str] == data.num)
{
test_mp[data.str] ++;
}
else
{
std::cout << "error" << std::endl;
std::cout << "consumer name is: " << data.str << std::endl;
std::cout << "consumer " << data.num << std::endl;
}
}
}
});
std::function<void(void)> finish_func = ([&queue](){
std::this_thread::sleep_for(std::chrono::seconds(10));
queue.Finish();
});
std::thread producer1(product_func, "producer1");
std::thread producer2(product_func, "producer2");
std::thread producer3(product_func, "producer3");
std::thread consumer1(consumer_func);
std::thread finish_thread(finish_func);
while (1)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
queue.Finish();
producer1.join();
producer2.join();
producer3.join();
consumer1.join();
finish_thread.join();
return 0;
}
設定佇列上限的執行緒安全佇列
需要兩個std::condition_variable變數,用於處理佇列為空和佇列為滿的情況。
增加了設定MaxSize的函式。
示例中,size=0表示佇列沒有上限。
示例2:設定佇列上限的執行緒安全佇列(包含測試程式碼)
#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <atomic>
#include <functional>
#include <string>
#include <chrono>
#include <map>
template <typename T>
class ThreadSafetyQueue
{
public:
ThreadSafetyQueue(size_t size = 0) :
finish_{false},
max_size_{size}
{
}
bool push(T &data)
{
std::unique_lock<std::mutex> lock(mtx_);
while (CheckFull() && !finish_)
{
// std::cout << "queue full" << std::endl;
full_cv_.wait(lock);
}
if(finish_)
{
// std::cout << "queue finish" << std::endl;
return false;
}
data_queue_.push(data);
lock.unlock(); // 解鎖在notify_one之前更合適
empty_cv_.notify_one();
return true;
}
bool pop(T &data)
{
std::unique_lock<std::mutex> lock(mtx_);
while(!finish_ && data_queue_.empty()) // 防止假醒
{
empty_cv_.wait(lock);
}
if(finish_)
{
// std::cout << "queue finish" << std::endl;
return false;
}
data = data_queue_.front();
data_queue_.pop();
full_cv_.notify_one();
return true;
}
bool empty()
{
std::unique_lock<std::mutex> lock(mtx_);
if(data_queue_.empty())
{
return true;
}
return false;
}
bool full()
{
std::unique_lock<std::mutex> lock(mtx_);
if(data_queue_.size() >= max_size_)
{
return true;
}
return false;
}
int size()
{
std::unique_lock<std::mutex> lock(mtx_);
return data_queue_.size();
}
void Finish()
{
finish_ = true;
full_cv_.notify_all();
empty_cv_.notify_all();
}
void SetMaxSize(size_t size)
{
std::unique_lock<std::mutex> lock(mtx_);
max_size_ = size;
lock.unlock();
full_cv_.notify_all();
}
private:
std::mutex mtx_;
std::queue<T> data_queue_;
std::condition_variable full_cv_;
std::condition_variable empty_cv_;
volatile size_t max_size_;
std::atomic_bool finish_;
bool CheckFull()
{
if(max_size_ == 0)
return false;
else
return data_queue_.size() >= max_size_;
}
};
struct test_data
{
int num;
std::string str;
};
int main()
{
ThreadSafetyQueue<test_data> queue(1000);
std::function<void(std::string)> product_func = ([&queue](std::string name){
for(int i = 0; i < 1000000; ++i)
{
test_data td{i, name};
queue.push(td);
}
});
std::function<void(void)> consumer_func = ([&queue](){
std::map<std::string, int> test_mp;
while(1)
{
test_data data;
if(queue.pop(data))
{
if(test_mp.find(data.str) == test_mp.end())
{
test_mp[data.str] = 0;
std::cout << "add consumer name is: " << data.str << std::endl;
}
if(test_mp[data.str] == data.num)
{
test_mp[data.str] ++;
}
else
{
std::cout << "error" << std::endl;
std::cout << "consumer name is: " << data.str << std::endl;
std::cout << "consumer " << data.num << std::endl;
}
}
}
});
std::function<void(void)> finish_func = ([&queue](){
std::this_thread::sleep_for(std::chrono::seconds(10));
queue.Finish();
});
std::thread producer1(product_func, "producer1");
std::thread producer2(product_func, "producer2");
std::thread producer3(product_func, "producer3");
std::thread consumer1(consumer_func);
std::thread finish_thread(finish_func);
while (1)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
queue.Finish();
producer1.join();
producer2.join();
producer3.join();
consumer1.join();
finish_thread.join();
return 0;
}