2010-10-20 94 views
0

看来这个问题getsaskedfrequently,但我没有得出任何明确的结论。我需要决定是否我应该有点帮助实现访问时,锁定代码/修改全局变量时,我有(或必须的!):在文件范围内 从单个工作线程更新全局变量:我需要互斥锁吗?

  • 一个“工人”定义

    • 全局变量线程读取/从主进程线程调用访问功能并返回这些全局

    写入全局变量

  • 通话所以现在的问题是,我应该锁定与互斥访问我的全局变量?

    更具体地说,我正在编写一个C++库,它使用网络摄像头跟踪纸张上的对象 - 计算机视觉是CPU密集型的,因此性能至关重要。我有一个单个工作线程在Open()函数中被分离出来。该线程处理所有的对象跟踪。当调用一个Close()函数时,它被终止(间接带有全局标志)。

    这感觉就像我只是要求内存损坏,但我没有观察到死锁问题,也没有遇到任何从这些访问函数返回的错误值。经过几个小时的研究,我得到的一般印象是,“呃,可能吧,无论如何,。”如果我确实应该使用互斥体,为什么我还没有遇到任何问题呢?

    这里是我目前的计划过于简单化:

    // *********** 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(); 
    } 
    

    是否有我应该采取的另一种方法?这个非问题问题让我今天非常紧张: - /我需要确保这个库是稳定的并且返回准确的值。任何有识之士将不胜感激。

    感谢

  • +0

    也许有些基础会有所帮助:http://en.wikipedia.org/wiki/Readers-writer_lock – JoshD 2010-10-21 00:07:21

    +0

    感谢大家的出色答案,尤其是暗示我对读者 - 作家互斥的想法(似乎这些概念没有提供)在大学里得到很多报道......)。我最终使用boost :: shared_mutex来锁定对主程序循环中修改的每个全局变量的访问。 (请参阅http://stackoverflow.com/questions/989795/example-for-boost-shared-mutex-multiple-reads-one-write)。如果有人感兴趣,我可以提供我最终解决方案的一个简单例子。 – 2010-10-22 22:36:51

    回答

    1

    我想这取决于你在DoWork()函数中做什么。我们假设它将点值写入p1。至少,你有一个竞争条件,将返回无效结果主线程以下可能性:

    假设工作线程要更新P1的值。例如,让我们将p1的值从(A,B)更改为(C,D)。这将涉及至少两个操作,将C存储在x中,将D存储在y中。如果主线程决定在GetPointer()函数中读取p1的值,则它还必须执行至少两个操作,即为x加载值并为y加载值。如果操作的序列是:

    1. 更新线程:器C
    2. 主线程:载荷X(主线程接收C)
    3. 主线程:负载Y(主线程接收B)
    4. 更新线程:存储D

    主线程将得到点(C,B),这是不正确的。

    这个特殊的问题是没有很好的利用线程,因为主线程没有做任何实际工作。我会使用一个线程和一个像WaitForMultipleObjectsEx这样的API,它允许您同时等待来自键盘stdin句柄的输入,来自摄像机的I/O事件和超时值。

    +0

    感谢您解释这个概念。虽然很难验证这是否发生在我的程序中,但我可以在测试程序中创建这个无效条件(使用线程休眠命令来指示长操作)。虽然对于最终用户来说,如果我的'x'和'y'值在一两次迭代时就像这样碎片化了,我可能无所谓了! :-)最终,我使用Boost库的'shared_mutex'和编写器优先锁......我所要做的就是重构整个程序,呃! – 2010-10-22 22:43:21

    5

    如果只有一个线程访问(读取和写入),那么就需要一个对象没有锁。

    如果一个对象是只读的,那么不需要锁。 (假设您可以保证在构建过程中只有一个线程访问该对象)。

    如果有任何线程写入(更改状态)的对象。如果有其他线程访问该对象,则所有访问(包括读取和写入)都必须锁定。尽管您可能会使用允许多个阅读器的读锁。但写入操作必须是独占的,在状态改变时,读者不能访问该对象。

    +0

    +1 ...除非,当然,你可以自动更新状态没有锁;-) – 2010-10-21 02:32:12

    1

    你不会遇到死锁,但是你可能会看到一些偶然的错误值,并且发生概率极低:因为读写的时间只有纳秒的几分之一,而你每秒只读取变量50次,碰撞约为5000万。

    如果在Intel 64上发生这种情况,“指针”与8字节的边界对齐,并且在一次操作中读取和写入(所有8个字节有一个汇编指令),然后访问是原子的,不需要互斥体。

    如果任这些条件不能满足,还有一种可能性,即读者会得到错误的数据。

    我把一个互斥体只是为了安全起见,因为它只会使用50次,第二和它不会成为一个性能问题。

    +2

    你的一些假设是可疑的。他的代码似乎不大可能以原子方式更新x和y成员,但我想如果CPU支持64位原子存储操作,编译器可以这样优化它。另一个问题是没有内存屏障,不能保证主线程必须读取主线程中的内存,并且不能保证更新线程实际上必须写入内存。如果他可以利用他的CPU/OS提供的原子操作,那么他不需要互斥锁。 – karunski 2010-10-21 00:36:36

    0

    由于指针中信息的性质,您可能不会看到问题。如果它正在跟踪一些运动速度不是很快的对象的坐标,并且在读取过程中位置被更新,那么坐标可能是“小关”,但不足以注意到。

    例如,假设一个更新后,PX为100,PY是100,您将可以追踪被摄体移动一点,这样在下次更新后,PX是102和PY是102.如果你碰巧读在本次更新的中期,之后x被更新,但y被更新之前,你最终会得到像素为102的指针值,以及点py为100

    1

    的情况非常清楚切 - 读者可能不会看到更新直到某些东西触发同步(互斥体,内存屏障,原子操作...)。许多事物过程确实隐含地触发了这种同步 - 例如,外部函数调用(由于理由解释了Usenet线程常见问题解答(http://www.lambdacs.com/cpt/FAQ.html) - 请参阅Dave Butenhof的回答需要易失性,所以如果您的代码处理的值很小足够的,他们不能被半写(例如数字而不是字符串,固定地址而不是动态(重新)分配),那么它可以在没有显式同步的情况下缓慢进行。

    如果你的表现想法是通过你的编写代码获得更多的循环,那么如果你忽略了同步,你会得到一个更好的数字。但是,如果您有兴趣将平均和最差情况的延迟降至最低,以及读者可以看到实际看到的多少更新,那么您应该从作者那里进行同步。

    +0

    感谢您的意见。您的评论推动了我的努力,并说服我实施MultiReader-SingleWriter锁定我的变量。 – 2010-10-22 22:46:18

    相关问题