(我讨厌这样的标题。但是我找不到更好的东西)
我有两个有两个线程的类。第一个检测两帧之间的运动:
void Detector::run(){
isActive = true;
// will run forever
while (isActive){
//code to detect motion for every frame
//.........................................
if(isThereMotion)
{
if(number_of_sequence>0){
theRecorder.setRecording(true);
theRecorder.setup();
// cout << " motion was detected" << endl;
}
number_of_sequence++;
}
else
{
number_of_sequence = 0;
theRecorder.setRecording(false);
// cout << " there was no motion" << endl;
cvWaitKey (DELAY);
}
}
}
第二个将在开始时录制视频:
void Recorder::setup(){
if (!hasStarted){
this->start();
}
}
void Recorder::run(){
theVideoWriter.open(filename, CV_FOURCC('X','V','I','D'), 20, Size(1980,1080), true);
if (recording){
while(recording){
//++++++++++++++++++++++++++++++++++++++++++++++++
cout << recording << endl;
hasStarted=true;
webcamRecorder.read(matRecorder); // read a new frame from video
theVideoWriter.write(matRecorder); //writer the frame into the file
}
}
else{
hasStarted=false;
cout << "no recording??" << endl;
changeFilemamePlusOne();
}
hasStarted=false;
cout << "finished recording" << endl;
theVideoWriter.release();
}
布尔记录由函数改变:
void Recorder::setRecording(bool x){
recording = x;
}
目标是在检测到动作时开始录制,同时阻止程序开始录制两次。
真正没有任何意义的真正奇怪的问题是代码只有在我输入布尔记录时才会起作用(标有&#34; +++++++ #34)。其他记录永远不会变为false,而else语句中的代码永远不会被调用。
有没有人知道为什么会这样。我还是刚开始使用c ++,但这个问题对我来说似乎很奇怪..
答案 0 :(得分:2)
我认为您的变量isThereMotion
和recording
是bool
类型的简单类成员。
默认情况下,对这些成员的并发访问不是线程安全的,您将面临竞争条件和各种奇怪的行为。
我建议像这样声明这些成员变量(只要你能使用最新的标准):
class Detector {
// ...
std::atomic<bool> isThereMotion;
};
class Recorder {
// ...
std::atomic<bool> hasStarted;
};
等
幕后的原因是,即使读/写一个简单的布尔值也会分成几个应用于CPU的汇编程序指令,并且可以在中间调度这些指令以进行进程的线程执行路径更改。使用std::atomic<>
可以自动为此变量提供类似关键部分的读/写操作。
简而言之:创建所有内容,用于从不同线程,原子值同时访问,或使用适当的同步机制,如std::mutex
。
如果您无法使用最新的c ++标准,则可以使用boost::thread
解决方法,以保持代码的可移植性。
注意:强>
从您的评论中,您的问题似乎特定于Qt框架,您可以使用许多机制进行同步,例如:提到的QMutex
。
为什么volatile
在多线程环境中没有帮助?
volatile
阻止编译器仅通过以前以顺序方式设置的值的假设来优化实际的读取访问。在实际检索或写入值时,它不会阻止线程中断。
volatile
应该用于读取可以独立于顺序或线程执行模型进行更改的地址(例如,总线寻址外设HW寄存器,其中HW主动更改值,例如FPGA报告当前数据吞吐量寄存器接口)。
在此处查看有关此误解的更多详情:
Why is volatile not considered useful in multithreaded C or C++ programming?
答案 1 :(得分:0)
您可以使用带有指向帧缓冲区的节点池作为链接列表fifo消息传递系统的一部分,使用互斥锁和信号量来协调线程。每个要记录的帧的消息将被发送到记录线程(附加到它的列表并释放信号量),否则该节点将被返回(附加)回主线程的列表。
使用基于Windows的同步复制文件的示例代码。主线程读入缓冲区,生成的线程从它接收的缓冲区写入。设置代码很长,但实际的消息传递函数和两个线程函数都很简单。
答案 2 :(得分:-2)
可能是一个活跃的问题。编译器可以重新排序指令或将isActive提升出循环。尝试将其标记为volatile
。
来自MSDN文档:
声明为volatile的对象不会在某些优化中使用,因为它们的值可能随时更改。系统始终在请求时读取volatile对象的当前值,即使前一条指令要求来自同一对象的值也是如此。此外,对象的值在分配时立即写入。
简单示例:
#include <iostream>
using namespace std;
int main() {
bool active = true;
while(active) {
cout << "Still active" << endl;
}
}
组装: g ++ -S test.cpp -o test1.a
将{volatile}添加到active
,如volatile bool active = true
再次组合g++ -S test.cpp -o test2.a
并查看差异diff test1.a test2.a
< testb $1, -5(%rbp)
---
> movb -5(%rbp), %al
> testb $1, %al
请注意,在测试之前,第一个人甚至懒得读取active
的值,因为循环体永远不会修改它。第二个版本。