无法弄清楚比赛状况在哪里发生

时间:2010-06-14 06:24:48

标签: c++ boost multithreading valgrind

我正在使用Valgrind --tool = drd来检查我的应用程序使用Boost :: thread。 基本上,应用程序根据通过套接字连接的输入,使用“Kehai”值填充一组“Book”值。

在单独的帖子中,用户可以连接并将书籍发送给他们。

它相当简单,所以我想在序列化书籍的位置使用boost :: mutex :: scoped_lock,清除书籍数据的位置应足以防止任何竞争条件。这是代码:

 void Book::clear()
    {
     boost::mutex::scoped_lock lock(dataMutex);
     for(int i =NUM_KEHAI-1; i >= 0; --i)
     {
      bid[i].clear();

      ask[i].clear();
     }
    }

    int Book::copyChangedKehaiToString(char* dst) const
    {
     boost::mutex::scoped_lock lock(dataMutex);

     sprintf(dst, "%-4s%-13s",market.c_str(),meigara.c_str());
     int loc = 17;
     for(int i = 0; i < Book::NUM_KEHAI; ++i)
     {
      if(ask[i].changed > 0)
      {
       sprintf(dst+loc,"A%i%-21s%-21s%-21s%-8s%-4s",i,ask[i].price.c_str(),ask[i].volume.c_str(),ask[i].number.c_str(),ask[i].postTime.c_str(),ask[i].status.c_str());
       loc += 77;
      }
     }
     for(int i = 0; i < Book::NUM_KEHAI; ++i)
     {
      if(bid[i].changed > 0)
      {
       sprintf(dst+loc,"B%i%-21s%-21s%-21s%-8s%-4s",i,bid[i].price.c_str(),bid[i].volume.c_str(),bid[i].number.c_str(),bid[i].postTime.c_str(),bid[i].status.c_str());
       loc += 77;
      }
     }

     return loc;
    }

clear()函数和copyChangedKehaiToString()函数分别在datagetting线程和数据发送线程中调用。 另外,作为一个注释,类Book:

    struct Book
    {
    private:
     Book(const Book&); Book& operator=(const Book&);
    public:

     static const int NUM_KEHAI=10;
     struct Kehai;
     friend struct Book::Kehai;

     struct Kehai
     {
     private:
       Kehai& operator=(const Kehai&);
     public:
      std::string price;
      std::string volume;
      std::string number;
      std::string postTime;
      std::string status;

      int changed;
      Kehai();
      void copyFrom(const Kehai& other);
      Kehai(const Kehai& other);
      inline void clear()
      {

       price.assign("");
       volume.assign("");
       number.assign("");
       postTime.assign("");
       status.assign("");
       changed = -1;
      }
     };

     std::vector<Kehai> bid;
     std::vector<Kehai> ask;
     tm recTime;
     mutable boost::mutex dataMutex;


     Book();
     void clear();
     int copyChangedKehaiToString(char * dst) const;
      };

当使用valgrind --tool = drd时,我会遇到竞争条件错误,如下所示:

==26330== Conflicting store by thread 1 at 0x0658fbb0 size 4
==26330==    at 0x653AE68: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (in /usr/lib/libstdc++.so.6.0.8)
==26330==    by 0x653AFC9: std::string::_M_replace_safe(unsigned int, unsigned int, char const*, unsigned int) (in /usr/lib/libstdc++.so.6.0.8)
==26330==    by 0x653B064: std::string::assign(char const*, unsigned int) (in /usr/lib/libstdc++.so.6.0.8)
==26330==    by 0x653B134: std::string::assign(char const*) (in /usr/lib/libstdc++.so.6.0.8)
==26330==    by 0x8055D64: Book::Kehai::clear() (Book.h:50)
==26330==    by 0x8094A29: Book::clear() (Book.cpp:78)
==26330==    by 0x808537E: RealKernel::start() (RealKernel.cpp:86)
==26330==    by 0x804D15A: main (main.cpp:164)
==26330== Allocation context: BSS section of /usr/lib/libstdc++.so.6.0.8
==26330== Other segment start (thread 2)
==26330==    at 0x400BB59: pthread_mutex_unlock (drd_pthread_intercepts.c:633)
==26330==    by 0xC59565: pthread_mutex_unlock (in /lib/libc-2.5.so)
==26330==    by 0x805477C: boost::mutex::unlock() (mutex.hpp:56)
==26330==    by 0x80547C9: boost::unique_lock<boost::mutex>::~unique_lock() (locks.hpp:340)
==26330==    by 0x80949BA: Book::copyChangedKehaiToString(char*) const (Book.cpp:134)
==26330==    by 0x80937EE: BookSerializer::serializeBook(Book const&, std::string const&) (BookSerializer.cpp:41)
==26330==    by 0x8092D05: BookSnapshotManager::getSnaphotDataList() (BookSnapshotManager.cpp:72)
==26330==    by 0x8088179: SnapshotServer::getDataList() (SnapshotServer.cpp:246)
==26330==    by 0x808870F: SnapshotServer::run() (SnapshotServer.cpp:183)
==26330==    by 0x808BAF5: boost::_mfi::mf0<void, RealThread>::operator()(RealThread*) const (mem_fn_template.hpp:49)
==26330==    by 0x808BB4D: void boost::_bi::list1<boost::_bi::value<RealThread*> >::operator()<boost::_mfi::mf0<void, RealThread>, boost::_bi::list0>(boost::_bi::type<void>, boost::_mfi::mf0<void, RealThread>&, boost::_bi::list0&, int) (bind.hpp:253)
==26330==    by 0x808BB90: boost::_bi::bind_t<void, boost::_mfi::mf0<void, RealThread>, boost::_bi::list1<boost::_bi::value<RealThread*> > >::operator()() (bind_template.hpp:20)
==26330== Other segment end (thread 2)
==26330==    at 0x400B62A: pthread_mutex_lock (drd_pthread_intercepts.c:580)
==26330==    by 0xC59535: pthread_mutex_lock (in /lib/libc-2.5.so)
==26330==    by 0x80546B8: boost::mutex::lock() (mutex.hpp:51)
==26330==    by 0x805473B: boost::unique_lock<boost::mutex>::lock() (locks.hpp:349)
==26330==    by 0x8054769: boost::unique_lock<boost::mutex>::unique_lock(boost::mutex&) (locks.hpp:227)
==26330==    by 0x8094711: Book::copyChangedKehaiToString(char*) const (Book.cpp:113)
==26330==    by 0x80937EE: BookSerializer::serializeBook(Book const&, std::string const&) (BookSerializer.cpp:41)
==26330==    by 0x808870F: SnapshotServer::run() (SnapshotServer.cpp:183)
==26330==    by 0x808BAF5: boost::_mfi::mf0<void, RealThread>::operator()(RealThread*) const (mem_fn_template.hpp:49)
==26330==    by 0x808BB4D: void boost::_bi::list1<boost::_bi::value<RealThread*> >::operator()<boost::_mfi::mf0<void, RealThread>, boost::_bi::list0>(boost::_bi::type<void>, boost::_mfi::mf0<void, RealThread>&, boost::_bi::list0&, int) (bind.hpp:253)

对于我的生活,我无法弄清楚比赛状况在哪里。据我所知,只有在使用互斥锁之后才能清除kehai,并且将其复制到字符串也是如此。 有没有人有任何想法可能导致这个,或者我应该在哪里?

谢天谢地。

4 个答案:

答案 0 :(得分:6)

在你的帖子之后,我花时间了解Valgrind及其输出应该如何阅读。

我可以看到以下内容:

您调用Book::clear,然后调用Book::Kehai::clear,在其中为字符串赋值。在std::string::assign内部,STL会在地址0x0658fbb0处存储一些值。

同时另一个线程访问了相同的内存位置,因此这种情况被视为竞争条件。

现在看一下另一个线程的“上下文”。 Valgrind没有显示其确切的堆栈位置,但是它显示了它之间发生的“段”。根据Valgrind,段是由同步操作限制的连续内存访问块。

我们在pthread_mutex_unlock看到此块以<{1}}开始结束。 意思是 - 当您的互斥锁未被锁定时访问了相同的内存位置,并且该线程位于您的两个函数之外的某个位置。

现在,查看冲突的内存位置信息:

pthread_mutex_lock

BSS意味着它是一个全局/静态变量。它在libstdc中的某个地方定义。

结论:

此竞争条件与您的数据结构无关。它与STL有关。一个线程对Allocation context: BSS section of /usr/lib/libstdc++.so.6.0.8 执行某些操作(确切地将其分配给空字符串),而另一个线程也可能执行与STL相关的操作。

BTW我记得几年前我写了一个多线程的应用程序,那里有std::string的问题。我发现 - STL实现(Dunkimware)实际上实现了字符串作为引用计数,而引用计数线程安全。

也许这也发生在你身上? 也许你应该在构建多线程应用程序时设置一些编译器标志/选项?

答案 1 :(得分:2)

此报告可能会被安全忽略。它是由如何在libstdc ++中实现std :: string触发的。此问题已在gcc 4.4.4或更高版本附带的libstdc ++版本中得到解决。有关详细信息,另请参阅GCC bugzilla item #40518

答案 2 :(得分:0)

没关系。我是个白痴,并设法忘记C ++字符串是可变的。 我改变了代码以使用c风格的字符串,我的竞争条件问题就消失了。

对于阅读这篇文章的人来说,有没有人知道C ++的一个好的不可变字符串库?我认为加强有一个,但我还没有找到任何结论。

感谢。

答案 3 :(得分:0)

  

STL应该是线程安全的,因为如果你正确锁定或者你只是执行多线程读取,将它们与线程一起使用就没有问题

Surpirse!是的,它是所谓的,但是让我告诉你一家商店实际上发生了什么

我有一个多线程的应用程序。有一个带字符串的数据结构(std::string)。它被一个关键部分锁定了。

其他对象最终需要从那里获取字符串。他们以下列方式复制了这些字符串:

// Take a string
std::string str;
{
    Autolock l(g_CritSect);
    str = g_SomeStr;
}

使用相同的策略来调整这些字符串:

// Put a string
std::string str;
{
    Autolock l(g_CritSect);
    g_SomeStr = str;
}

而且,猜猜怎么着?崩溃!

但为什么呢?因为字符串的赋值语句并不真正生成包含字符串的内存块的副本。而是重用(引用)相同的内存块。

嗯,这不一定是坏事。但糟糕的是std :: string实现了字符串的引用计数,而不是以线程安全的方式。它使用常规算术++和 - 而不是InterlockedIncrement等等。

发生的事情是str对象引用了相同的字符串。然后它最终在析构函数中解除引用(或者在显式分配给另一个字符串时)。这发生在锁定区域的外部

因此,这些字符串可能无法在多线程应用程序中使用。并且几乎不可能实现正确的锁定来解决这个问题,因为实际引用的数据会在对象之间静默传递。

STL的实现被声明为线程安全。