**已解决:在我的类的构造函数中,我进行了信号量的构造竞赛和线程的构造,在此我希望先创建信号量,然后再创建线程。对我有用的解决方案是首先在基类中创建信号量,这样我可以在派生类中依赖它。 **

我有一个很小的pthreads C++程序,在正常情况下可以正常工作。但是,在程序上使用valgrind的线程错误检查工具时,似乎发现了竞争情况。使得这种竞争情况特别难以避免的原因是,它发生在“信号量”类(实际上只是封装了sem_init,sem_wait和sem_post)内,所以我不能用另一个信号量来解决此问题(并且不必)。我不认为valgrind会给出错误的肯定,因为我的程序在valgrind下运行时会表现出不同的行为。

这是Semaphore.cpp *:

#include“Semaphore.h”
#include
#include
#include

信号量::信号量(bool pshared,int initial)
:m_Sem(新sem_t())
{
如果(m_Sem == 0)
抛出std::runtime_error(“信号量构造函数错误:m_Sem == 0”);
if(sem_init(m_Sem,(pshared?1:0),initial)==-1)
抛出std::runtime_error(“sem_init失败”);
}

信号量:: ~~ Semaphore()
{
sem_destroy(m_Sem);
删除m_Sem;
}
无效信号量:: lock()
{
如果(m_Sem == 0)
抛出std::runtime_error(“Semaphore::lock错误:m_Sem == 0”);

int rc;
对于(;;){
rc = sem_wait(m_Sem);
if(rc == 0)中断;
if(errno == EINTR)继续;
抛出std::runtime_error(“sem_wait失败”);
}
}
无效信号量:: unlock()
{
if(sem_post(m_Sem)!= 0)
抛出std::runtime_error(“sem_post失败”);
}

  • 请注意,Semaphore的构造函数如何创建一个新的名为“m_Sem”的sem_t,并在m_Sem仍等于0的极不可能的情况下引发异常。这仅意味着该构造函数应该不允许m_Sem等于0。 。继续进行Semaphore::lock:无论从哪个线程(以及构造函数)调用此函数,从理论上讲,m_Sem仍应为0还是不可能的,对吧?好吧,当我在helgrind下运行程序时,Semaphore::lock肯定会抛出此异常“Semaphore::lock错误:m_Sem == 0”,我真的认为这应该是不可能的。

  • 我在没有通过helgrind的其他程序中使用了此Semaphore类,并且我真的不确定在此引起问题的特别之处。根据helgrind的说法,竞争发生在一个线程中的Semaphore构造函数中的写入与另一个线程中的Semaphore::lock的读取之间。老实说,我什至不知道这是怎么可能的:对象的方法如何与该对象的构造函数产生竞争条件? C++不能保证可以在对象上调用方法之前已调用构造函数吗?即使在多线程环境中,也怎么能违反呢?

    无论如何,现在用于valgrind输出。我使用的是Valgind版本“Valgrind-3.6.0.SVN-Debian”。 Memcheck说一切都很好。这是赫尔格林的结果:

    $ valgrind --tool = helgrind --read-var-info =是./try
    == 7776 == Helgrind,线程错误检测器
    == 7776 ==版权所有(C)2007-2009,由GNU GPL授权,由OpenWorks LLP等提供。
    == 7776 ==使用Valgrind-3.6.0.SVN-Debian和LibVEX;使用-h重新运行以获取版权信息
    == 7776 ==命令:./ try
    == 7776 ==
    抛出实例'== 7776 ==后调用终止,线程#1是程序的根线程
    == 7776 ==
    == 7776 ==线程2已创建
    == 7776 == at 0x425FA38:clone(clone.S:111)
    == 7776 ==通过0x40430EA:pthread_create @@ GLIBC_2.1(createthread.c:249)
    == 7776 ==通过0x402950C:pthread_create_WRK(hg_intercepts.c:230)
    == 7776 ==通过0x40295A0:pthread_create @ *(hg_intercepts.c:257)
    == 7776 ==通过0x804CD91:Thread::Thread(void *(*)(void *),void *)(Thread.cpp:10)
    == 7776 ==通过0x804B2D5:ActionQueue::ActionQueue()(ActionQueue.h:40)
    == 7776 ==通过0x80497CA:main(try.cpp:9)
    == 7776 ==
    == 7776 ==在线程#1以0x42ee04c大小写入4的过程中可能发生数据争用
    == 7776 == at 0x804D9C5:Semaphore::Semaphore(bool,int)(Semaphore.cpp:8)
    == 7776 ==通过0x804B333:ActionQueue::ActionQueue()(ActionQueue.h:40)
    == 7776 ==通过0x80497CA:main(try.cpp:9)
    == 7776 ==这与线程#2先前读取的大小4冲突
    == 7776 ==在0x804D75B:Semaphore::lock()(Semaphore.cpp:26)
    == 7776 ==通过0x804B3BE:Lock::Lock(Semaphore&)(Lock.h:17)
    == 7776 ==通过0x804B497:ActionQueue::ActionQueueLoop()(ActionQueue.h:56)
    == 7776 ==通过0x8049ED5:void * CallMemFun,&(ActionQueue::ActionQueueLoop())>(void *)(CallMemFun.h:7)
    == 7776 ==通过0x402961F:mythread_wrapper(hg_intercepts.c:202)
    == 7776 ==通过0x404296D:start_thread(pthread_create.c:300)
    == 7776 ==通过0x425FA4D:克隆(clone.S:130)
    == 7776 ==
    std::runtime_error'
    what():信号量::锁定错误:m_Sem == 0
    == 7776 ==
    == 7776 ==有关检测到的和抑制的错误的计数,请重新运行:-v
    == 7776 ==使用--history-level = approx或= none可以提高速度
    == 7776 ==降低访问冲突信息准确性的成本
    == 7776 ==错误摘要:1个上下文中有1个错误(抑制:5个中的5个)

    拥有git和valgrind的任何人都可以通过从我的git repo分支中 check out 代码来重现此代码(据记录,该分支当前正在提交262369c2d25eb17a0147),如下所示:

    $ git clone git://github.com/notfed/concqueue -b信号量
    $ cd征服
    $使
    $ valgrind --tool = helgrind --read-var-info =是./try

    最佳答案

    尽管看起来线程在线程1完成运行构造函数之前尝试在线程2中使用信号量。在这种情况下,可以使m_Sem为NULL(0)或任何其他值。

    09-06 11:23