потоки posix и оптимизация O3 - PullRequest
0 голосов
/ 29 июля 2011

Я работаю над программой, использующей mpi (openmpi 1.4.3) и pthreads, работающей в c ++ под linux.

некоторые узлы mpi имеют систему массового обслуживания, реализованную с помощью pthreads. Идея - это простой поток, добавляющий элементы в очередь, и несколько других «работающих» потоков, выбирающих объекты и выполняющих над ними свою работу (не ракетостроение).

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

    while (true){
        if (t_exitSignal[tID]){
            dorun = false;
            break;
        }

        //cout<<"w8\n";

        //check if queue has some work for us
        if (!frame_queue->empty()){

            //try to get lock and recheck that queue no empty
            pthread_mutex_lock( &mutex_frame_queue );

            if (!frame_queue->empty()){
                cout<<"Pickup "<<tID<<endl;
                con = frame_queue->front();
                frame_queue->pop();
                t_idling[tID] = false;
                pthread_mutex_unlock( &mutex_frame_queue );
                break;
            }

            pthread_mutex_unlock( &mutex_frame_queue );
        }

    }

Теперь рассмотрим этот, точно такой же код, за исключением того, что мьютекс gettimg заблокирован, прежде чем я проверю очередь-> empthy. Эта работа отлично работает для всех уровней оптимизации.

    while (true){
        if (t_exitSignal[tID]){
            dorun = false;
            break;
        }
        //cout<<"w8\n";

        //try to get lock and recheck that queue no empty
        pthread_mutex_lock( &mutex_frame_queue );

        //check if queue has some work for us
        if (!frame_queue->empty()){

                cout<<"Pickup "<<tID<<endl;
                con = frame_queue->front();
                frame_queue->pop();
                t_idling[tID] = false;
                pthread_mutex_unlock( &mutex_frame_queue );
                break;

        }
        pthread_mutex_unlock( &mutex_frame_queue );

    }

На всякий случай, если что-то изменится, так я заполняю очередь из другого потока

                    pthread_mutex_lock( &mutex_frame_queue );
            //adding the same contianer into queue to make it available for threads
            frame_queue->push(*cursor);
            pthread_mutex_unlock( &mutex_frame_queue );

У меня вопрос: почему перестает работать первый пример кода, почему я компилирую с опцией -O3? Любое другое предложение для системы массового обслуживания?

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

РЕШЕНИЕ: Это то, что я придумал в конце. Кажется, работает намного лучше, чем любой из методов выше. (на всякий случай, если кому-то интересно;)

    while (true){

        if (t_exitSignal[tID]){

            dorun = false;
            break;
        }
        //try to get lock and check that queue no empty
        pthread_mutex_lock( &mutex_frame_queue );

        if (!frame_queue->empty()){

            con = frame_queue->front();
            frame_queue->pop();
            t_idling[tID] = false;
            pthread_mutex_unlock( &mutex_frame_queue );
            break;
        }else{

            pthread_cond_wait(&conf_frame_queue, &mutex_frame_queue);
            pthread_mutex_unlock( &mutex_frame_queue );
        }




    }

Добавление

        pthread_mutex_lock( &mutex_frame_queue );

        //adding the same contianer into queue to make it available for threads
        frame_queue->push(*cursor);
        //wake up any waiting threads
        pthread_cond_signal(&conf_frame_queue);
        pthread_mutex_unlock( &mutex_frame_queue )

Ответы [ 2 ]

2 голосов
/ 29 июля 2011

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

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

Кроме того, вы рассматривали условные переменные pthread ?Они позволят вам избежать опроса в цикле, пока ваш контейнер больше не опустеет.

1 голос
/ 29 июля 2011

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

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