生产者和使用者函数,用于在操作手册中测试C++并发的线程安全堆栈示例
Producer and consumer functions for test thread-safe stack examples of C++ concurrency in action book
我已经开始学习并发(C++11),阅读《C++并发在行动》一书。如何测试线程安全堆栈类(示例取自操作清单 3.5 中的C++并发)。我希望对生产者/消费者函数有不同的实现,让我测试它的所有函数。
#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();
}
};
int main()
{
//test class
return 0;
}
你只需要:
- 从主函数创建堆栈
- 启动一个将填充堆栈的线程(将堆栈对象指针作为参数传递给线程,并通过一直调用 push 使线程执行填充堆栈的 for 循环)
- 然后,当此线程运行时,从主程序的另一个循环中清空堆栈
如果您只想进行快速测试并且不知道如何在创建时将对象传递给线程,您也可以将堆栈声明为全局变量。
如果您需要干净退出,请添加一个传递给线程的atomic
(编辑,我首先建议volatile
)bool,告诉它您已完成并要求它停止循环。然后使用 join 等待线程退出。
结构的最小测试驱动程序可能如下所示:
struct Msg {
size_t a;size_t b;size_t c;size_t d;
};
bool isCorrupted(const Msg& m) {
return !(m.a == m.b && m.b == m.c && m.c == m.d);
}
int main()
{
threadsafe_stack<Msg> stack;
auto prod = std::async(std::launch::async, [&]() {
for (size_t i = 0; i < 1000000; ++i){
Msg m = { i, i, i, i };
stack.push(m);
//std::this_thread::sleep_for(std::chrono::microseconds(1));
if (i % 1000 == 0) {
std::cout << "stack.push called " << i << " times " << std::endl;
}
}
});
auto cons = std::async(std::launch::async, [&]() {
for (size_t i = 0; i < 1000000; ++i){
try {
Msg m;
stack.pop(m);
if (isCorrupted(m)) {
std::cout << i <<" ERROR: MESSAGE WAS CORRUPED:" << m.a << "-" << m.b << "-" << m.c << "-" << m.d << std::endl;
}
if (i % 1000 == 0) {
std::cout << "stack.pop called " << i << " times " << std::endl;
}
}
catch (empty_stack e) {
std::cout << i << " Stack was empty!" << std::endl;
}
}
});
prod.wait();
cons.wait();
return 0;
}
请注意,这不会测试所有不同的函数,也不会测试所有可能的竞争条件,因此您必须扩展它。
关于课程设计的两点建议:
1) 当堆栈为空时,我不会抛出异常,因为这在异步场景中非常常见。而是让使用者线程等待(请参阅条件变量)或分别返回 false 或 nullptr。
2)在pop()
函数中使用std::unique_ptr
而不是std::shared_ptr<T>
,因为它更有效率,而且您无论如何都不会在这里共享任何内容。
相关文章:
- 控制允许动态运行c++的并发操作数
- 节俭并发:未解决的外部问题
- 并发/多线程:是否可以以这种方式生成相同的输出?
- 用于在并发环境中访问 MMIO 的软件模式
- C++中的并发哈希表
- 提升 asio 并发计时器取消问题与链
- C++具有基元类型的并发队列
- 使用简单两相锁定的并发程序
- C++ 按引用或值推送的并发队列
- 允许多个互斥锁所有者或指定数量的并发代码执行
- 琐碎并发代码的吞吐量不会随着线程数量的增加而增加
- 由并发无序映射查找线程调用的函数是否安全?
- 在迭代期间并发修改映射
- 并发安全堆栈接口方法:正确与否?
- 内存模型和并发
- 重塑Microsoft的并发::d iagnostic::span,也可以检测外部跨度
- 如何在 qt 中从另一个线程运行 qt并发时关闭程序
- 并发问题:如何只有一个线程通过关键部分
- Qt并发错误:用作初始值设定项的数组
- 如何使用 c++ 实现并发文件/文本编辑?