【发布时间】:2017-12-02 23:50:43
【问题描述】:
我正在使用 C++ 处理多线程项目,我怀疑 std::mutex
假设我有一个堆栈。
#include <exception>
#include <memory>
#include <mutex>
#include <stack>
struct empty_stack: std::exception
{
const char* what() const throw();
};
template<typename T>
class threadsafe_stack
{
private:
std::stack<T> data;
mutable std::mutex m;
public:
threadsafe_stack(){}
threadsafe_stack(const threadsafe_stack& other)
{
std::lock_guard<std::mutex> lock(other.m);
data=other.data;
}
threadsafe_stack& operator=(const threadsafe_stack&) = 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();
}
};
有人说使用这个堆栈可以避免竞争条件。但是我认为这里的问题是互斥锁,也就是互斥,这里只确保单个功能不在一起。例如,我可以让线程调用 push 和 pop。这些函数仍然存在竞争条件问题。
例如:
threadsafe_stack st; //global varibale for simple
void fun1(threadsafe_stack st)
{
std::lock_guard<std::mutex> lock(m);
st.push(t);
t = st.pop();
//
}
void fun2(threadsafe_stack st)
{
std::lock_guard<std::mutex> lock(m);
T t,t2;
t = st.pop();
// Do big things
st.push(t2);
//
}
如果一个线程 fun1 和 fun2 调用同一个栈(简单来说是全局变量)。所以它可能是一个竞争条件(?)
我认为唯一的解决方案是使用某种原子事务手段,而不是直接调用 push()、pop()、empty(),而是通过一个带有指向这些函数的“函数指针”的函数来调用它们,并且只有一个互斥锁。
例如:
#define PUSH 0
#define POP 1
#define EMPTY 2
changeStack(int kindOfFunction, T* input, bool* isEmpty)
{
std::lock_guard<std::mutex> lock(m);
switch(kindOfFunction){
case PUSH:
push(input);
break;
case POP:
input = pop();
break;
case EMPTY:
isEmpty = empty();
break;
}
}
我的解决方案好吗?或者我只是想多了,我朋友告诉我的第一个解决方案就足够了?有没有其他解决方案?该解决方案可以像我建议的那样避免“原子事务”。
【问题讨论】:
-
为什么你认为如果你从线程中调用pop/push,就会出现竞争条件?线程 A 调用 push 并阻塞 mutex,线程 B 调用 pop 并等待 mutex 被 push 解除阻塞。
-
您正在尝试解决一个非问题。 “mutex aka 互斥在这里只确保单个功能不在一起。” --- 这是错误的假设。
-
按照您编写
fun1和fun2的方式,永远不会存在竞争条件 --- 您通过值(即副本)而不是通过引用传递对象。跨度>
标签: c++ multithreading mutex