Проблема с cond.signal или lock.release? - PullRequest
0 голосов
/ 11 января 2011

Привет, когда я запускаю следующий код, я обнаружил, что поток сигнализации продолжает работать в течение долгого времени, прежде чем другой поток запускается ... почему?разве проснувшийся поток не должен запускаться, как только сигнализатор снимает блокировку?Или ОС занимает много времени, чтобы вернуть спящий поток в очередь готовности?.

#include pthread.h

#include stdio.h

#include stdlib.h

void stupidfunction1(void *arg);

void stupidfunction2(void *arg);

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;

int thread1count,thread2count;

int thread1waiting = 0;

int thread2waiting = 0;

void main()

{
    printf("Hello World\n");

    pthread_t thread1,thread2;

    int i;

    thread1count = 0;

    thread2count = 0;


    i = pthread_create(&thread1,NULL,&stupidfunction1,NULL);

    i = pthread_create(&thread2,NULL,&stupidfunction2,NULL);

    pthread_join(thread1,NULL);

    pthread_join(thread2,NULL);

    printf("Done with everythinh");


}


void stupidfunction1(void *arg)

{
int i = 0;

    for(i = 0;i<50;i++)
    {

    thread1count++;

    pthread_mutex_lock(&mutex1);

    if((thread1count-thread2count)>5)

    {
           thread1waiting = 1;

               printf("thread1 waiting \n");    

               pthread_cond_wait(&cond1,&mutex1);

               thread1waiting = 0;
    }

        else if((thread2waiting == 1) && abs(thread1count-thread2count)<1)

    {

        printf("signalling thread2\n");

        pthread_cond_signal(&cond1);

    }

    pthread_mutex_unlock(&mutex1);

    printf("Hey its thread 1 @  %d\n",thread1count);
    }
}


void stupidfunction2(void *arg)
{
int i = 0;

    for(i = 0;i<50;i++)
    {

    thread2count++;

    pthread_mutex_lock(&mutex1);

    if((thread2count-thread1count)>5)
    {
               thread2waiting = 1;

           printf("thread2 waiting \n");    

               pthread_cond_wait(&cond1,&mutex1);

               thread2waiting = 0;
    }

        else if((thread1waiting == 1) && abs(thread1count-thread2count)<1)
    {

        printf("signalling thread1\n");

        pthread_cond_signal(&cond1);
    }

    pthread_mutex_unlock(&mutex1);

    printf("Hey its thread 2 @  %d\n",thread2count);
    }
}

ВЫХОД:

Hey its thread 2 @  1

Hey its thread 2 @  2

Hey its thread 2 @  3

Hey its thread 2 @  4

Hey its thread 2 @  5

thread2 waiting 

Hey its thread 1 @  1

Hey its thread 1 @  2

Hey its thread 1 @  3

Hey its thread 1 @  4

Hey its thread 1 @  5

signalling thread2

Hey its thread 1 @  6

Hey its thread 1 @  7

Hey its thread 1 @  8

Hey its thread 1 @  9

Hey its thread 1 @  10

Hey its thread 1 @  11

Ответы [ 3 ]

2 голосов
/ 12 января 2011

При прямом ответе на ваш вопрос: нет, pthread_mutex_unlock и pthread_cond_signal не будите сразу ожидающей ветки.Вместо этого они могут просто пометить его как «готовый к запуску», и тогда ОС будет планировать пробужденный поток, когда это будет похоже на это.Конечно, ОС может принять решение о немедленном переключении на этот поток (особенно, если он имеет более высокий приоритет, чем любой выполняющийся в данный момент поток), но это может быть не так.: у вас могут быть запущены оба потока одновременно!

То, что возвращается pthread_cond_wait, не означает, что переменная условия была сигнализирована.Это называется "ложным следом".Чтобы правильно использовать pthread_cond_wait, вы должны поместить его в цикл, где проверяется состояние, связанное с пробуждением, пока удерживается мьютекс, непосредственно перед вызовом pthread_cond_wait.например,

void wait_until_signalled(int* wake_flag,pthread_cond_t* cond,pthread_mutex_t* mutex)
{
    pthread_mutex_lock(&mutex);
    while(!(*wake_flag)) /* if the int pointed to by wake_flag is non-zero then wake up */
    {
        pthread_cond_wait(&cond,&mutex);
    }
    pthread_mutex_unlock(&mutex);
}

void signal(int* wake_flag,pthread_cond_it* cond,pthread_mutex_t* mutex)
{
    pthread_mutex_lock(&mutex);
    *wake_flag=1; /* tell the waiting thread that it should wake */
    pthread_mutex_unlock(&mutex);
    pthread_cond_signal(&cond); /* wake up the thread if it is blocked in pthread_cond_wait*/
}

Конечно, вы, вероятно, захотите проверить возвращаемые значения вызовов pthread_xxx.

Так как значение, на которое указывает wake_flag, проверяется и изменяется только с помощьюмьютекс заблокирован, тогда ожидающий поток обязательно проснется, если он установлен, и не вернется из wait_until_signalled, пока не будет установлен флаг.Вызов pthread_cond_wait атомарно помечает поток как ожидающий и разблокирует мьютекс, поэтому либо вызов pthread_cond_signal увидит, что поток ожидает, и разбудит его, чтобы он мог проверить флаг (который уже установлен), илипоток не ожидает, что означает, что он должен заблокировать мьютекс ПОСЛЕ того, как поток в signal установит флаг, и в этом случае ожидающий поток увидит установленный флаг и вернется.

0 голосов
/ 13 января 2011

Потоки запускаются, когда планировщик приступает к их выполнению.

Способ, которым один поток 2 должен работать быстро: когда поток 1 сигнализирует, что поток 2 просыпается (и в вашем случае много раз до того, как на самом деле это делает поток 2), все потоки, ожидающие переменную условия, должны бороться за блокировку, когда планировщик запускает их. В вашем примере, я уверен, что планировщик никогда не попадет в поток 2 до его завершения. Хорошая ставка в том, что ваш временной интервал составляет 1 мс.

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

В качестве примера все еще предполагается, что поток 2 является ожидающим потоком.

Предположим, вы хотите, чтобы поток 2 немедленно проснулся ... AFAIK, вы ничего не можете сделать.

Если вы действительно хотели, чтобы поток 1 остановился после сигнализации, и вы все еще хотели использовать pthread_cond (есть и другие более быстрые способы!), Вы могли бы разблокировать мьютекс перед передачей ( это позволило, я думаю, ) и затем вызовите sched_yield () или nanosleep ({0,0}) сразу после сигнала. Плохо для производительности, потому что поток 1 просто помещает себя в конец строки потоков (с тем же приоритетом), ожидающих запуска. Это увеличит ваше отношение переключений контекста к времени выполнения. Это также плохо из-за дополнительной разблокировки / блокировки, которую вы должны сделать. Помимо всего этого, поток 1 может снова проснуться, прежде чем поток 2! Вы должны будете использовать цикл, продолжать сигнализировать и уступать, пока этот чертов поток 2 не сделает свою работу! Klunky!

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

веселит.

0 голосов
/ 11 января 2011

Здесь я вижу две ошибки:

  • Вы не блокируете мьютекс вокруг pthread_cond_signal. Это означает, что сигнал может быть отправлен во время, когда другой поток находится между проверкой, должен ли он продолжать работать, и точкой, в которой он переходит в pthread_cond_wait, поэтому сигнал может здесь потеряться.

  • Общие переменные должны быть изменчивыми. Компилятор может решить перенести доступ за пределы цикла или частично развернуть цикл и пропустить доступ между двумя экземплярами тела цикла.

Ни один из них напрямую не объясняет симптом, который вы видите; однако данное поведение абсолютно в допустимых пределах стандарта.

...