本例的stack在接口设计上和std::stack略有不同,是std::stack的一个基于std::mutex的简单封装。
本例合并了top和pop接口统一到pop,因为在top和pop之间如果有其他操作,无法纳入std::mutex管辖之内,只能勉强将其合并。
程序代码如下,
conanfile.txt
[requires]
boost/1.72.0
[generators]
cmake
CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(3_5_thread_safe_stack)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig/")
set ( CMAKE_CXX_FLAGS "-pthread")
set(CMAKE_CXX_STANDARD 17)
add_definitions(-g)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
include_directories(${INCLUDE_DIRS})
LINK_DIRECTORIES(${LINK_DIRS})
file( GLOB main_file_list ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
foreach( main_file ${main_file_list} )
file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${main_file})
string(REPLACE ".cpp" "" file ${filename})
add_executable(${file} ${main_file})
target_link_libraries(${file} ${CONAN_LIBS} pthread)
endforeach( main_file ${main_file_list})
main.cpp
#include <iostream>
#include <mutex>
#include <algorithm>
#include <functional>
#include <thread>
#include <stack>
#include <memory>
#include <exception>
struct empty_stack: public std::exception {
char const* what() const noexcept {
return "empty stack";
}
};
template <typename T>
class threadsafe_stack {
private:
std::stack<T> data;
mutable std::mutex m;
public:
threadsafe_stack() {}
threadsafe_stack(threadsafe_stack const& other) {
std::lock_guard<std::mutex> lock(other.m);
data = other.data;
}
// 不允许拷贝赋值
threadsafe_stack& operator=(threadsafe_stack const&) = delete;
void push(T new_value) {
std::lock_guard<std::mutex> lock(m);
data.push(new_value);
}
std::shared_ptr<T> pop() {
std::lock_guard<std::mutex> lock(m);
if(data.empty()) {
throw empty_stack();
}
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
data.pop();
return res;
}
void pop(T& value) {
std::lock_guard<std::mutex> lock(m);
if(data.empty()) {
throw empty_stack();
}
value = data.top();
data.pop();
}
bool empty() const {
std::lock_guard<std::mutex> lock(m);
return data.empty();
}
};
int main(int argc, char* argv[]) {
threadsafe_stack<int> si;
std::thread add_thread([&]() {
for(int i=0; i<10000; ++i) {
si.push(i);
}
});
std::thread pop_thread([&]() {
for(int i=0; i<10000; ++i) {
if(!si.empty()) {
std::cout << *(si.pop()) << std::endl;
}
}
});
add_thread.join();
pop_thread.join();
return EXIT_SUCCESS;
}
多线程操作后,并不能保证栈上10000个元素都先进后出,因为pop没有等到push完就开始操作了。但是threadsafe_stack能够保证数据整整是10000个,不会有重复的,错误的数据。
如果等到10000个元素都push完,就没有threadsafe的意义了,就是单线程操作了。
网友评论