Thread safe stack
Implement thread safe stack
Thread safe stack
Almost the same as thread safe queue.Check thread safe queue.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <iostream>
#include <condition_variable>
#include <mutex>
#include <stack>
#include <optional>
#include <thread>
#include <future>
#include <chrono>
template<typename T>
class ThreadSafeStack {
std::mutex m_;
std::condition_variable con_;
std::stack<T> stack_;
public:
ThreadSafeStack() = default;
//std::mutex is non-copyable
ThreadSafeStack(const ThreadSafeStack& other) = delete;
ThreadSafeStack& operator=(const ThreadSafeStack& other) = delete;
ThreadSafeStack(ThreadSafeStack&& other) noexcept {
std::lock_guard<std::mutex> l(other.m_);
stack_ = std::move(other.stack_);
}
ThreadSafeStack& operator=(ThreadSafeStack&& other) noexcept{
if(this != &other) {
std::scoped_lock<std::mutex> l(m_, other.m_);
stack_ = std::move(other.stack_);
}
return *this;
}
template<typename U>
void push(U&& item) {
{
std::lock_guard<std::mutex> l(m_);
stack_.push(std::forward<U>(item));
}
con_.notify_one();
}
//blocking pop, using con_ and lock
T pop() {
std::unique_lock<std::mutex> l(m_);
con_.wait(l, [this]() { return !stack_.empty();});
T item = std::move(stack_.top()); //safe since pop later, also efficient if T is large
stack_.pop();
return item;
}
//non blocking pop
std::optional<T> try_pop() {
std::lock_guard<std::mutex> l(m_);
if(stack_.empty()) return std::nullopt;
T item = std::move(stack_.top());
stack_.pop();
return item;
}
//blocking timeout
template<typename Rep, typename Period>
std::optional<T> pop_for(std::chrono::duration<Rep, Period> timeout){
std::unique_lock<std::mutex> l(m_);
if(!con_.wait_for(l, timeout, [this](){return !stack_.empty();}) )
return std::nullopt;
T item = std::move(stack_.top());
stack_.pop();
return item;
}
bool empty() const {
std::lock_guard<std::mutex> l(m_);
return stack_.empty();
}
size_t size() const {
std::lock_guard<std::mutex> l(m_);
return stack_.size();
}
};
template<typename T>
void pushq(ThreadSafeStack<T>& q, int a) {
std::this_thread::sleep_for(std::chrono::seconds(3));
q.push(a);
}
template<typename T>
T popq(ThreadSafeStack<T>& q) {
return q.pop();
}
template<typename T>
std::optional<T> try_popq(ThreadSafeStack<T>& q) {
return q.try_pop();
}
int main() {
ThreadSafeStack<int> tsq;
std::thread t1(pushq<int>, std::ref(tsq), 3);
std::future<int> f1 = std::async(std::launch::async, popq<int>, std::ref(tsq));
std::future<std::optional<int>> f2 = std::async(std::launch::async, try_popq<int>, std::ref(tsq));
std::cout<<f1.get()<<std::endl;
auto result = f2.get();
if(result) std::cout<<*result<<std::endl;
else std::cout<<"the stack is empty!"<<std::endl;
t1.join();
return 0;
}
This post is licensed under CC BY 4.0 by the author.