声明:本文参考自《C++并发编程实战(第二版)》
本文试图根据书中的代码在力所能及范围内实现一个queue类,虽然底层用了现成的queue,比较丑陋(请忽略)
struct empty_queue :std::exception
{
const char ch = 'n';
const char* what() const throw() { return &ch; }
};
template<class T>
class threadsafe_queue;
template<class T>
void swap(threadsafe_queue<T>& l, threadsafe_queue<T>& r);
template<class T>
bool operator==(threadsafe_queue<T>&, threadsafe_queue<T>&);
template<class T>
class threadsafe_queue
{
private:
queue<T>data;
mutable std::mutex mut;
mutable std::shared_mutex smut;
std::condition_variable dc;
public:
threadsafe_queue() = default;
threadsafe_queue(const threadsafe_queue& other)
{
std::lock_guard<std::mutex>lck(other.mut);
data = other.data;
}
std::mutex& get_mutex()
{
return this->mut;
}
queue<T>& get_data()
{
return this->data;
}
/*
_NODISCARD unsigned int size()
{
lock_guard<std::mutex>lck(mut);
return data.size();
}
*/
T front()
{
std::lock_guard<std::shared_mutex>lck(smut);
if (data.empty())
throw empty_queue();
return data.front();
}
std::shared_ptr<T>front(T)
{
std::lock_guard<std::shared_mutex>lck(smut);
if (data.empty())
throw empty_queue();
const std::shared_ptr<T>res(std::make_shared<T>(data.front()));
return res;
}
void push(const T& value)
{
std::lock_guard<std::mutex>lck(mut);
data.push(value);
dc.notify_one();
}
void push(T&& value)
{
std::lock_guard<std::mutex>lck(mut);
data.push(std::move(value));
dc.notify_one();
}
std::shared_ptr<T>pop()
{
std::lock_guard<std::mutex>lck(mut);
if (data.empty())
throw empty_queue();
const std::shared_ptr<T> res(std::make_shared<T>(data.front()));
data.pop();
return res;
}
void pop(T& value)
{
std::lock_guard<std::mutex>lck(mut);
if (data.empty())
throw empty_queue();
value = data.front();
data.pop();
}
void wait_and_pop(T& value)
{
std::unique_lock<std::mutex>lck(mut);
dc.wait(lck, [this] {return !data.empty(); });
value = data.front();
data.pop();
}
std::shared_ptr<T>wait_and_pop()
{
std::unique_lock<std::mutex>lck(mut);
dc.wait(lck, [this] {return !data.empty(); });
std::shared_ptr<T>res(std::make_shared<T>(data.front()));
data.pop();
return res;
}
bool try_pop(T& value)
{
std::lock_guard<std::mutex>lk(mut);
if (!data.empty())
return false;
value = data.front();
data.pop();
return true;
}
std::shared_ptr<T>try_pop()
{
std::lock_guard<std::mutex>lck(mut);
if (data.empty())
return std::shared_ptr<T>();
std::shared_ptr<T>res(std::make_shared<T>(data.front()));
data.pop();
return res;
}
bool empty()const
{
std::lock_guard<std::mutex>lck(mut);
return data.empty();
}
void swap(threadsafe_queue<T>& other) noexcept
{
// c++11 or c++14
//lock(this->m, other.m);
//std::lock_guard<std::mutex>lck1(other.m, std::adopt_lock);
//std::lock_guard<std::mutex>lck2(this->m, std::adopt_lock);
// c++17
std::scoped_lock guard(this->mut, other.mut);
this->data.swap(other.data);
}
friend void swap(threadsafe_queue<T>& l, threadsafe_queue<T>& r)
{
// c++11 or c++14
//std::lock(l.get_mutex(), r.get_mutex());
//std::lock_guard<std::mutex>lck1(l.get_mutex(), std::adopt_lock);
//std::lock_guard<std::mutex>lck2(l.get_mutex(), std::adopt_lock);
// c++17
std::scoped_lock guard(l.get_mutex(), r.get_mutex());
std::swap(l.get_data(), r.get_data());
}
bool operator==(const threadsafe_queue<T>& oth)
{
// c++11 or c++14
//std::lock(l.get_mutex(), r.get_mutex());
//std::lock_guard<std::mutex>lck1(l.get_mutex(), std::adopt_lock);
//std::lock_guard<std::mutex>lck2(l.get_mutex(), std::adopt_lock);
// c++17
std::scoped_lock guard(mut, oth.mut);
return data == oth.data;
}
friend bool operator==(threadsafe_queue<T>& l, threadsafe_queue<T>& r)
{
// c++11 or c++14
//std::lock(l.get_mutex(), r.get_mutex());
//std::lock_guard<std::mutex>lck1(l.get_mutex(), std::adopt_lock);
//std::lock_guard<std::mutex>lck2(l.get_mutex(), std::adopt_lock);
// c++17
std::scoped_lock guard(l.get_mutex(), r.get_mutex());
return l.get_data() == r.get_data();
}
};
测试代码如下:
void oper(threadsafe_queue<int>& s)
{
for (int i = 0; i < 100; i++)
{
s.push(i);
cout << "push : " << i << endl;
}
for (int i = 0; i < 50; i++)
{
shared_ptr<int> ptr = s.pop();
cout << "pop : " << *ptr << endl;
}
for (int i = 0; i < 25; i++)
{
int j = 0;
s.wait_and_pop(j);
cout << "wait_and_pop : " << j << endl;
}
for (int i = 0; i < 25; i++)
{
shared_ptr<int> g_res = s.wait_and_pop();
cout << "s_wait_and_pop : " << *g_res << endl;
}
if (s.empty())
cout << "empty" << endl;
}
int main()
{
threadsafe_queue<int>s1;
threadsafe_queue<int>s2;
thread obj1(oper, ref(s1));
thread obj2(oper, ref(s2));
obj1.join();
obj2.join();
threadsafe_queue<int> s3(s2);
thread obj3(oper, ref(s3));
obj3.join();
threadsafe_queue<int>s4;
for (int i = 0; i < 5; i++)
s4.push(i);
threadsafe_queue<int>s5;
for (int i = 5; i < 10; i++)
s5.push(i);
swap(s4, s5);
while (!s4.empty())
{
int i = 0;
int tmp = s4.front();
cout << tmp << " ";
s4.pop(i);
cout << i << " " << endl;
}
s4.swap(s5);
while (!s4.empty())
{
int i = 0;
shared_ptr<int>tmp = s4.front(i);
cout << *tmp << " ";
s4.pop(i);
cout << i << " " << endl;
}
threadsafe_queue<int>s6;
for (int i = 0; i < 5; i++)
s6.push(i);
threadsafe_queue<int>s7;
for (int i = 0; i < 5; i++)
s7.push(i);
bool flg1 = (s6 == s7);
bool flg2 = s6.operator==(s7);
cout << flg1 << " " << flg2 << endl;
threadsafe_queue<int>s8;
for (int i = 0; i < 15; i++)
s8.push(i);
for (int i = 0; i < 5; i++)
{
int t = 0;
bool res = s8.try_pop(t);
if (res)
cout << t << endl;
else
cout << "empty" << endl;
}
for (int i = 0; i < 5; i++)
{
shared_ptr<int>g_res = s8.try_pop();
cout << *g_res << endl;
}
return 0;
}
文章展示了如何基于《C++并发编程实战(第二版)》中的代码实现一个线程安全的队列`threadsafe_queue`,该队列使用了内置的队列、互斥锁和条件变量来保证并发环境下的正确性。队列提供了`push`、`pop`、`front`等操作,并处理了空队列的情况。此外,还提供了交换和等于操作的实现。
10万+

被折叠的 条评论
为什么被折叠?



