C++ 从单个工作线程更新全局变量:我需要互斥锁吗?
这似乎是一个问题,但我不会得出任何明确的结论。我需要一点帮助来决定我是应该还是必须!在访问/修改全局变量时,在以下情况下执行锁定代码: 在文件范围内定义的全局变量 单个工作线程读取/写入全局变量 来自主进程线程的调用,调用返回这些全局变量的访问器函数 所以问题是,我应该用互斥锁锁定对全局变量的访问吗 更具体地说,我正在编写一个C++库,它使用一个摄像头来跟踪一个纸页上的对象——计算机视觉是CPU密集型的,所以性能是至关重要的。我有一个工作线程,它是在一个开放函数中分离出来的。此线程处理所有对象跟踪。当调用Close函数时,它通过全局标志间接终止 感觉上我只是在要求内存损坏,但我没有发现死锁问题,也没有遇到从这些访问器函数返回的任何错误值。经过几个小时的研究,我得到的总体印象是,嗯,可能吧。无论什么祝你玩得开心。如果我真的应该使用互斥体,为什么我还没有遇到任何问题呢 以下是对我当前计划的过度简化:C++ 从单个工作线程更新全局变量:我需要互斥锁吗?,c++,multithreading,opencv,mutex,C++,Multithreading,Opencv,Mutex,这似乎是一个问题,但我不会得出任何明确的结论。我需要一点帮助来决定我是应该还是必须!在访问/修改全局变量时,在以下情况下执行锁定代码: 在文件范围内定义的全局变量 单个工作线程读取/写入全局变量 来自主进程线程的调用,调用返回这些全局变量的访问器函数 所以问题是,我应该用互斥锁锁定对全局变量的访问吗 更具体地说,我正在编写一个C++库,它使用一个摄像头来跟踪一个纸页上的对象——计算机视觉是CPU密集型的,所以性能是至关重要的。我有一个工作线程,它是在一个开放函数中分离出来的。此线程处理所有对象跟
// *********** lib.h ***********
// Structure definitions
struct Pointer
{
int x, y;
};
// more...
// API functions
Pointer GetPointer();
void Start();
void Stop();
// more...
实现如下所示
// *********** lib.cpp ***********
// Globals
Pointer p1;
bool isRunning = false;
HANDLE hWorkerThread;
// more...
// API functions
Pointer GetPointer()
{
// NOTE: my current implementation is actually returning a pointer to the
// global object in memory, not a copy of it, like below...
// Return copy of pointer data
return p1;
}
// more "getters"...
void Open()
{
// Create worker thread -- continues until Close() is called by API user
hWorkerThread = CreateThread(NULL, 0, DoWork, NULL, 0, NULL);
}
void Close()
{
isRunning = false;
// Wait for the thread to close nicely or else you WILL get nasty
// deadlock issues on close
WaitForSingleObject(hWorkerThread, INFINITE);
}
DWORD WINAPI DoWork(LPVOID lpParam)
{
while (isRunning)
{
// do work, including updating 'p1' about 10 times per sec
}
return 0;
}
最后,从外部可执行文件调用此代码。类似这样的伪代码:
// *********** main.cpp ***********
int main()
{
Open();
while ( <esc not pressed> )
{
Pointer p = GetPointer();
<wait 50ms or so>
}
Close();
}
我是否应该采取不同的方法?这个非问题今天让我抓狂:-/我需要确保这个库是稳定的,并且返回准确的值。如有任何见解,将不胜感激
谢谢如果只有一个线程同时读取和写入对象,则不需要锁 如果对象是只读的,则不需要锁。假设您可以保证在构造期间只有一个线程访问对象
如果任何线程写入,将更改对象的状态。如果有其他线程访问该对象,则必须锁定所有读写访问。尽管您可以使用允许多个读卡器的读锁。但是写入操作必须是独占的,并且在状态更改时,任何读卡器都不能访问对象。不会出现死锁,但您可能会偶尔看到一些极低概率的坏值:因为读取和写入只需几分之一纳秒,而您每秒只读取变量50次,碰撞的可能性大约是5000万分之一 如果在Intel 64上发生这种情况,指针将与8字节边界对齐,并在一次操作中读取和写入所有8字节,使用一条汇编指令,则访问是原子的,不需要互斥 如果这两个条件中的任何一个都不满足,那么读卡器就有可能得到错误的数据
为了安全起见,我会设置一个互斥锁,因为它每秒只会使用50次,不会造成性能问题。我想这取决于您在DoWork函数中所做的操作。假设它将一个点值写入p1。至少您存在以下可能的竞态条件,该竞态条件将向主线程返回无效结果: 假设工作线程想要更新p1的值。例如,让我们将p1的值从A、B更改为C、D。这将至少涉及两个操作,将C存储在x中,将D存储在y中。如果主线程决定在GetPointer函数中读取p1的值,那么它还必须执行至少两个操作,x的load value和y的load value。如果操作顺序为: 更新线程:storec 主线程:加载x主线程接收C 主线程:加载y主线程接收B 更新线程:存储D 主线程将获得不正确的点C、B
这个特殊的问题不是线程的好用途,因为主线程没有做任何实际的工作。我将使用一个线程和一个API,比如WaitForMultipleObjectsEx,它允许您同时等待来自键盘stdin句柄的输入、来自相机的I/O事件和超时值。由于指针中信息的性质,您可能看不到问题。如果它正在跟踪某个移动速度不是很快的对象的坐标,并且在读取过程中更新了位置,则坐标可能有点偏离,但不足以引起注意
例如,假设更新后,p.x为100,p.y为100。您正在跟踪的对象移动了一点,因此在下一次更新后,p.x为102,p.y为102。如果你恰好在这个更新的中间阅读,在X被更新,但是在Y被更新之前,你将得到一个指针值为Px为102,Py为100的指针值。 < P>这种情况是相当清楚的-在某些触发同步的过程中,读者可能看不到更新。 互斥、内存屏障、原子操作。。。。进程所做的许多事情都会隐式触发这种同步-例如,Usenet线程常见问题解答中解释的出于原因的外部函数调用http://www.lambdacs.com/cpt/FAQ.html -请参阅Dave Butenhof的答案,我们需要volatile,因此,如果您的代码所处理的值足够小,不能写一半,例如数字而不是字符串,固定地址而不是动态重新分配,那么它可以在没有显式同步的情况下缓慢运行
如果您对性能的想法是通过编写代码获得更多的循环,那么如果不考虑同步,您将得到一个更好的数字。但是如果你感兴趣的是最小化平均和最坏情况下的延迟,以及读者CAM实际上看到了多少不同的更新,那么你应该和作者同步。也许一些基金会会有帮助:谢谢大家的精彩回答,特别是向我暗示读者-作者互斥的想法,似乎这些概念在大学里没有得到太多的报道。。。。我最终使用了boost::shared_互斥锁来锁定对主程序循环中修改的每个全局变量的访问。看见如果有人感兴趣,我可以提供一个我最终解决方案的简单例子。你的一些假设是有问题的。他的代码似乎不太可能以原子方式更新x和y成员,尽管我认为如果CPU支持64位原子存储操作,编译器可以以这种方式对其进行优化。另一个问题是,如果没有内存屏障,就不能保证主线程必须读取主线程中的内存,也不能保证更新线程实际上必须写入内存。如果他可以利用CPU/OS提供的原子操作,那么他就不需要互斥体了。+1…当然,除非你可以在没有锁的情况下原子地更新状态-谢谢你解释这个概念。虽然很难验证这是否发生在我的程序中,但我能够在测试程序中使用线程休眠命令来指示长操作,从而创建此无效条件。如果我的x和y值在一两次迭代中像这样支离破碎,对最终用户来说可能无关紧要,但我不能让它过去最终,我使用了Boost库的shared_互斥锁和writer优先锁……我所要做的就是重组整个程序,啊!谢谢你的意见。你的评论把我逼到了极点,说服我在变量上实现多读单写器锁。