从另一个对象指向线程对象:危险
Pointing to thread object from another object: dangerous?
假设有两个对象都继承自thread
父类"使用pthreads的utility thread "
class Othread1: public thread
{
public:
start() { /* launch thread at 10 Hz */ };
end();
void setvar(float vr) {var2= vr } ;
protected :
float var1;
}
和
class Othread2: public thread
{
start() { /* launch the thread at 1000 Hz */ } ;
end();
float getvar() { return var2 } ;
protected :
float var2;
}
有这样一种东西我们可以这样做吗?
void threadManager(thread *th1, thread *th2)
{
float vtemp = th2->getvar();
th1->setvar(vtemp);
}
int main ()
{
thread th1;
thread th2;
threadManager(&th1,&th2);
return 0;
}
这样的线程间数据使用是一个安全的事情吗?或者我必须使用生产者/消费者模式进行队列来交换数据吗?
我仍然不完全确定你在做什么,但这里有一个例子,希望能帮助你。
如果你想在一个线程中读取数据,同时在另一个线程中写入数据,你需要同步,否则你会调用未定义的行为。"写作事件"answers"阅读事件"之间的时间长短并不重要。就语言规则而言,两个同步点之间发生的一切都是"同时的"。
这方面的明确规则可以在§1.10 [intro]中找到。多线程]的N4140, c++ 14标准的最终草案。但那里使用的语言很难破译。
一个更非正式的解释可以在Bjarne Stroustrup的 c++编程语言(第4版)的§41.2.4中找到。
如果两个线程可以同时访问一个内存位置,并且至少有一个访问是写操作,那么两个线程就存在数据竞争。请注意,精确地定义"同时"并不简单。如果两个线程有数据竞争,没有语言保证:行为是未定义的。
就我而言,我认为第一句中的"can"是假的,不应该出现在那里,但我引用的是这本书的原样。
保护互访问的经典方法是使用mumus和锁。由于c++ 11(并且仅从c++ 11开始,c++才有了并发性的定义),标准库为此提供了std::mutex
和std::lock_guard
(都在<mutex>
头文件中定义)。
如果您有简单的类型,如整数,使用锁是多余的。现代硬件支持对这些简单类型进行原子操作。标准库为此提供了std::atomic
类模板(在<atomic>
头文件中定义)。你可以在任何普通的可复制类型上使用它。
这是一个相当无用的例子,我们有两个线程分别执行一个函数writer
和reader
。writer
有一个伪随机数生成器,并定期要求它生成一个新的随机整数,并自动存储在全局变量value
中。reader
周期性地自动加载value
的值,并推进它自己的伪随机数生成器,直到它赶上。第二个全局原子变量done
由主线程使用,在两个线程应该停止时向它们发出信号。注意,我已经把你的赫兹换成了千赫兹,这样等待程序执行就不那么无聊了。
#include <atomic>
#include <chrono>
#include <random>
#include <thread>
namespace /* anonymous */
{
std::atomic<bool> done {};
std::atomic<int> value {};
void
writer(const std::chrono::microseconds period)
{
auto rndeng = std::default_random_engine {};
auto rnddst = std::uniform_int_distribution<int> {};
while (!done.load())
{
const auto next = rnddst(rndeng);
value.store(next);
std::this_thread::sleep_for(period);
}
}
void
reader(const std::chrono::microseconds period)
{
auto rndeng = std::default_random_engine {};
auto rnddst = std::uniform_int_distribution<int> {};
auto last = 0;
while (!done.load())
{
const auto next = value.load();
while (last != next)
last = rnddst(rndeng);
std::this_thread::sleep_for(period);
}
}
}
int
main()
{
using namespace std::chrono_literals;
std::thread writer_thread {writer, 100us}; // 10 kHz
std::thread reader_thread {reader, 10us}; // 100 kHz
std::this_thread::sleep_for(3s);
done.store(true);
writer_thread.join();
reader_thread.join();
}
如果你有一个现代的GCC或Clang,你可以(也许应该)用-fsanitize=thread
开关编译你的调试版本。如果运行这样编译的二进制文件,并且它执行数据竞争,编译器添加的特殊工具将输出有用的错误消息。试着用一个普通的int value
替换上面程序中的std::atomic<int> value
,看看工具会报告什么。
如果你还没有c++ 14,你不能使用字面后缀,但必须拼出std::chrono::microseconds {10}
等等。
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- CMake-按正确顺序将项目与C运行时对象文件链接
- 空基优化子对象的地址
- 将对象数组的引用传递给函数
- 你能重载对象变量名本身返回的内容吗
- C++使用整数的压缩数组初始化对象
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 将对象移动到std::shared_ptr
- 代理对象的常量正确性
- 提升 ASIO 无法识别计时器对象
- 将Ref对象作为类成员
- 将包含C样式数组的对象初始化为成员变量(C++)
- 如何返回一个类的两个对象相加的结果
- 既然存在危险,为什么项目要使用-I include开关
- 在对象构造期间,将指向尚未构造的子对象的指针传递给另一个子对象的构造函数是否危险?
- 对象切片:通过按值派生为 Base - 安全还是危险?
- 在不先显式调用析构函数的情况下,在旧对象上使用placement new是否危险
- 将const char*赋值给string对象有危险吗?
- 从另一个对象指向线程对象:危险