Не может выяснить, где состояние состязания происходит

Я использую Valgrind - tool=drd для проверки моего приложения, которое использует Повышение:: поток. В основном приложение заполняет ряд "Книжных" значений со значениями "Kehai" на основе исходных данных посредством сокетного соединения.

На отдельном потоке пользователь может соединиться и добраться, книги отправляют им.

Его довольно простое, таким образом, я изобразил использование повышения:: взаимное исключение:: 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;
    }

Ясные () функция и copyChangedKehaiToString () функция называют в потоке datagetting и потоке отправки данных, соответственно. Кроме того, как примечание, класс Книга:

    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 сделано только взяв взаимное исключение, и то же сохраняется с копированием его к строке. У кого-либо есть какие-либо идеи, что могло вызывать это, или где я должен посмотреть?

Большое спасибо.

7
задан Nik 14 June 2010 в 06:24
поделиться

3 ответа

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

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

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

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

Теперь посмотрите на "контекст" другого потока. Valgrind не показывает точное местоположение стека, но показывает, между какими "сегментами" это произошло. Согласно 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.

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

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

6
ответ дан 7 December 2019 в 05:18
поделиться

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
ответ дан 7 December 2019 в 05:18
поделиться

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

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

Спасибо.

0
ответ дан 7 December 2019 в 05:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: