Программа boost :: threads вызывает много условий гонки - PullRequest
3 голосов
/ 02 ноября 2011

У меня есть программа, в которой я использую boost :: threads для многопоточности. К сожалению, drd (valgrind --tool=drd ./my_program) сообщает о многих проблемах около 10000.

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

Я пытаюсь передать определенные фильтры и хочу, чтобы их можно было запустить, вызвав последний фильтр с помощью run. Затем этот фильтр должен сначала вызвать весь свой фильтр-предшественник, от которого он зависит, и в конце вызвать его метод processQueue (). Я хочу теперь иметь возможность вызывать фильтры-прекурсоры в их выигрышном потоке, чтобы ускорить график, если график будет параллельным. Поэтому я добавил группу потоков, чтобы каждый фильтр предшественника выполнялся в своем собственном потоке. Но, к сожалению, я получаю много условий гонки, где я не уверен, откуда они происходят. Надеюсь теперь стало понятнее, чего я хочу добиться.

Обновление

Я обновил код до еще более простого кода, где проблема все еще возникает. Я думаю, что проблема где-то связана с генерацией потока.

Обновление 2

Я думаю, что основной причиной этого является очень высокий уровень ложноположительных результатов валгринда. Я открыл новый вопрос по этому поводу. Смотрите здесь

Обновление 3

большинства ошибок можно избежать, если я использую valgrind 3.6.1 вместо 3.7.0 или 3.8.0.

Вот один доклад дрд:

==29905== Conflicting load by thread 1 at 0xb0081000 size 8
==29905==    at 0x25A6C2: pthread_join (in /usr/lib/system/libsystem_c.dylib)
==29905==    by 0x2BEC0: boost::thread::join() (in /usr/local/lib/libboost_thread.dylib)
==29905==    by 0x100006641: Filter::run() (in ./playgroudThreads)
==29905==    by 0x100001013: main (in ./playgroudThreads)
==29905== Allocation context: unknown.
==29905== Other segment start (thread 2)
==29905==    at 0x2A7B68: thread_start (in /usr/lib/system/libsystem_c.dylib)
==29905== Other segment end (thread 2)
==29905==    at 0x3E667A: mach_msg_trap (in /usr/lib/system/libsystem_kernel.dylib)
==29905==    by 0x3DED38: semaphore_create (in /usr/lib/system/libsystem_kernel.dylib)
==29905==    by 0x2A50F7: new_sem_from_pool (in /usr/lib/system/libsystem_c.dylib)
==29905==    by 0x2A6199: _pthread_exit (in /usr/lib/system/libsystem_c.dylib)
==29905==    by 0x2A48C9: _pthread_start (in /usr/lib/system/libsystem_c.dylib)
==29905==    by 0x2A7B74: thread_start (in /usr/lib/system/libsystem_c.dylib)

А вот мой пример кода:

#include <iostream>
#include <vector>
#include <sys/time.h>
#include <boost/thread.hpp>
#include <boost/bind.hpp>

class Filter
{
    public:

        Filter(int n) :
                n_(n), precursor_(0)
        {
        }

        ~Filter()
        {
        }

        void connect(Filter& f)
        {
            precursor_ = &f;
        }

        void run()
        {

            if (!isCalculationDone_) {
                if (precursor_) {
                    boost::thread thread(&Filter::run, precursor_);

                    thread.join();
                }
                this->processQueue(2);
                isCalculationDone_ = true;

            }

        }

        void processQueue(unsigned N)
        {
            //do some calculations

        }

    public:
        int n_;
        Filter* precursor_;

        bool isCalculationDone_;

};

int main(int argc, char* argv[])
{

    Filter* f1 = new Filter(1);
    Filter* f2 = new Filter(2);

    f2->connect(*f1);

    f2->run();

    std::cerr << "main: done" << std::endl;
    delete f2;
    delete f1;
    return 0;

}
;

Ответы [ 3 ]

2 голосов
/ 03 ноября 2011

Вы создаете 8 фильтров. Каждый объект Filter имеет свой собственный filterMutex_ - они не имеют ничего общего друг с другом.

Вы создаете более 8 тем. Это умышленно?

Каждый вызов run запускает новый поток для каждого предшественника, вызывая Filter :: run в этом потоке для этого объекта-предшественника Filter. Итак:

f8->run creates 2 threads for its precursors, calling f6->run and f7->run
 f6->run creates 2 threads: f4->run and f5->run
  f4->run creates 1 thread: f2->run
   f2->run creates 1 thread: f1->run
    f1->run creates no additional threads
  f5->run creates 1 thread: f3->run
   f3->run creates 1 thread: f1->run (different thread from the other f1->run)
    f1->run creates no additional threads
 f7->run creates 1 thread: f3->run
  f3->run creates 1 thread: f1->run
   f1->run creates no additional threads

Итак, с вашими 8 объектами Filter вы создаете 10 потоков (в дополнение к основному потоку), дважды вызываете f3->run и трижды f1->run.

Несколько вызовов на run для одного и того же объекта будут сериализованы. Различные фильтры не сериализуются.

Не уверен, что что-то из этого вызывает вашу проблему, но именно такие вещи заставляют меня задуматься о дизайне и о том, что он должен делать.

1 голос
/ 03 ноября 2011

Вы не одиноки: посмотрите thread здесь, что говорит о том, что проблема является ложным срабатыванием ", вероятно, вызванная повторным использованием памяти для локального хранения потока из завершенного потока вновь созданным потоком ».

0 голосов
/ 03 ноября 2011

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

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

Я предполагаю, что один из этих двух сценариев происходит в вашей программе, и поэтому vallgrind сообщает вам об этих проблемах, поэтому на вашей позиции я бы прошел весь ваш код и фактически пересмотрел бы любую зависимость, которая существует или может быть между любой новойобъявление.И учитывая основную часть:

f2->connect(f1);
f3->connect(f1);
f4->connect(f2);
f5->connect(f3);
f6->connect(f4);
f6->connect(f5);
f7->connect(f3);
f8->connect(f6);
f8->connect(f7);

и

boost::unique_lock<boost::shared_mutex> lock(filterMutex_);

Я думаю, это может быть первый сценарий.

Эта ссылка может помочь в интерпретации вашего вывода vallgrind.В частности, часть «8.2.9. Отладка программ OpenMP» может быть интересна для вас, поскольку на самом деле очень похожий вывод приведен в качестве примера.

Здесь учебное пособие, которое, кажется, фактически проходит всеэти сценарии (и даже несколько других) и довольно хорошо объясняют, как использовать boost-threading.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...