Не могу понять, где происходит гонка - PullRequest
7 голосов
/ 14 июня 2010

Я использую Valgrind --tool = drd, чтобы проверить мое приложение, которое использует Boost :: thread. По сути, приложение заполняет набор значений «Book» значениями «Kehai» на основе входных данных через сокетное соединение.

В отдельном потоке пользователь может подключаться и отправлять ему книги.

Это довольно просто, поэтому я решил использовать 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 () вызываются в потоке набора данных и в потоке отправки данных соответственно. Также, как примечание, класс 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 ]

6 голосов
/ 14 июня 2010

После вашего поста я потратил время, чтобы узнать о Valgrind и о том, как следует читать его вывод.

Я вижу следующее:

Вы вызываете Book::clear, что в свою очередь вызывает Book::Kehai::clear, где вы присваиваете значение строке.Внутри std::string::assign STL делает что-то, что хранит какое-то значение по адресу 0x0658fbb0.

Между тем другой поток получил доступ к той же ячейке памяти, поэтому эта ситуация считается состоянием гонки.

Теперь посмотрим на «контекст» другого потока.Valgrind не показывает точное местоположение стека, однако показывает, между какими «сегментами» он произошел.Согласно Вальгринду, сегмент - это последовательный блок обращений к памяти, ограниченный операциями синхронизации.

Мы видим, что этот блок начинается с pthread_mutex_unlock, а заканчивается в pthread_mutex_lock.Значит - к той же ячейке памяти обращались, когда ваш мьютекс не был заблокирован, и этот поток находился где-то вне ваших двух функций.

Теперь посмотрите на информацию о конфликтующей ячейке памяти:

Allocation context: BSS section of /usr/lib/libstdc++.so.6.0.8

BSS означает, что это глобальная / статическая переменная.И он определен где-то внутри libstdc.

Вывод:

Это условие гонки не имеет ничего общего с вашими структурами данных.Это связано с STL.Один поток выполняет что-то для std::string (точнее назначает его пустой строке), тогда как другой поток, вероятно, также делает что-то, связанное с STL.

Кстати, я помню, несколько лет назад я написалмногопоточное приложение, и там были проблемы с std::string.Как я выяснил, реализация STL (которая была Dunkimware) фактически реализовала строку как счетчик ссылок, тогда как счетчик ссылок был не поточно-ориентированным.

Возможно, именно это и происходит си вам того же?Возможно, вам следует установить флаг / опцию компилятора при создании многопоточного приложения?

2 голосов
/ 21 сентября 2010

Этот отчет может быть безопасно проигнорирован. Это вызвано тем, как std :: string реализован в libstdc ++. Эта проблема была решена в версии libstdc ++, включенной в gcc 4.4.4 или новее. Подробности см. Также Элемент bugzilla GCC # 40518 .

0 голосов
/ 14 июня 2010

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 была объявлена ​​поточно-безопасной.

0 голосов
/ 14 июня 2010

Nevermind.Я идиот и сумел забыть, что строки C ++ изменчивы.Я изменил код, чтобы использовать строки в стиле c, и мои проблемы с состоянием гонки исчезли.

Если кто-то читает этот пост, знает ли кто-нибудь хорошую неизменяемую библиотеку строк для C ++?Я думал, что у Boost есть один, но я не смог найти ничего убедительного в этом.

Спасибо.

...