gpt4 book ai didi

c++ - 为什么这段代码在 valgrind (helgrind) 下失败?

转载 作者:行者123 更新时间:2023-12-01 14:46:44 28 4
gpt4 key购买 nike

**已解决:在我类(class)的构造函数中,我有一个信号量的构造与线程的构造竞争,我希望先创建信号量,然后再创建线程。对我有用的解决方案是首先在基类中创建信号量,这样我就可以在派生类中依赖它。 **

我有一个相当小的 pthreads C++ 程序,它在正常条件下工作正常。然而,当在程序上使用 valgrind 的线程错误检查工具时,它似乎发现了一个竞争条件。使这种竞争条件特别难以避免的原因是它发生在“信号量”类中(它实际上只是封装了 sem_init、sem_wait 和 sem_post),所以我不能用另一个信号量来解决这个问题(也不应该)。我不认为 valgrind 会给出误报,因为我的程序在 valgrind 下运行时表现出不同的行为。

这是 Semaphore.cpp * :

#include "Semaphore.h"
#include
#include
#include

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

信号量::~信号量()
{
sem_destroy(m_sem);
删除 m_Sem;
}
void Semaphore::lock()
{
如果(m_Sem==0)
throw std::runtime_error("信号量::锁定错误:m_Sem == 0");

国际RC;
为了(;;){
rc = sem_wait(m_Sem);
如果(rc==0)中断;
if(errno==EINTR) 继续;
throw std::runtime_error("sem_wait 失败");
}
}
void Semaphore::unlock()
{
如果(sem_post(m_Sem)!=0)
throw 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 error: m_Sem==0”,我真的认为这应该是不可能的。

  • 我已经在其他程序中使用了这个 Semaphore 类,这些程序通过 helgrind 没有问题,我真的不确定我在这里做了什么特别的事情导致了这个问题。根据 helgrind 的说法,竞争发生在一个线程中的 Semaphore 构造函数中的写入和另一个线程中 Semaphore::lock 中的读取之间。老实说,我什至不明白这是怎么可能的:对象的方法如何与该对象的构造函数存在竞争条件? C++ 不保证在可以调用对象上的方法之前已经调用了构造函数吗?即使在多线程环境中,这怎么可能被违反?

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

    $ valgrind --tool=helgrind --read-var-info=yes ./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== 在 0x425FA38:克隆 (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== by 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== 在 0x804D9C5: Semaphore::Semaphore(bool, int) (Semaphore.cpp:8)
    ==7776== by 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== by 0x804B497:ActionQueue::ActionQueueLoop() (ActionQueue.h:56)
    ==7776== by 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== by 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 分支(记录在案,目前提交 262369c2d25eb17a0147)中的代码来重现这一点,如下所示:

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

    最佳答案

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

    关于c++ - 为什么这段代码在 valgrind (helgrind) 下失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3578998/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com