почему режим сна игнорировал несколько последовательных инструкций в моей программе?

0

Я применил пример условной переменной. Однако я вижу, что как только я сплю после получения сигнала pthread_cond_wait. Код после сна не выполняется. Он непосредственно переходит в состояние else. Я понимаю, что мьютекс теперь разблокирован. У меня есть только два потока A и B. Оба потока имеют две разные процедуры запуска. Если я поставил спать в потоке A после получения сигнала, тогда он должен запланировать поток B. Теперь, когда поток B будет выполнен. Поток A должен возобновиться после того, как он заснул. Однако он не возобновляет сон. Он повторно приобретает замок. См. Мой пример, и он выводится. Вы заметите, что следующие строки никогда не были напечатаны. Чтобы быстро устранить неполадку в моем issue-, просто посмотрите на следующую функцию: void * Thread_Function_A (void * thread_arg) и void * Thread_Function_B (void * thread_arg). Другие функции не так важны.

The cond_wait is unblocked now
The thread A proceeds
Thread A unlocked

Вот программа сейчас -

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
/** get pid **/
#include <sys/types.h>
#include <unistd.h>
/** kill signal **/
#include <signal.h>

using namespace std;

int shared_variable = 7;

pid_t pid_A;
pid_t pid_B;



class helium_thread
{
  private:
  pthread_t *thread_id;
  pid_t process_pid;

  public:
  static pthread_mutex_t mutex_thread;
  static pthread_cond_t cond_var;
  void set_thread_id(pthread_t tid);
  pthread_t *get_thread_id();
  int create_thread(pthread_t *thread_ptr, const pthread_attr_t *attr, void * (*start_routine)(void *), void *arg );
  helium_thread();  
  ~helium_thread();

};

helium_thread thread_1, thread_2;
/** The definition of the static member can't be inside a function, You need to put it outside **/
/** When I tried using inside a function, I got the error - error: invalid use of qualified-name ‘helium_thread::mutex_thread **/

pthread_mutex_t helium_thread::mutex_thread = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  helium_thread::cond_var = PTHREAD_COND_INITIALIZER;


void helium_thread::set_thread_id( pthread_t tid)
{
   *(this->thread_id) = tid;    
}

pthread_t * helium_thread::get_thread_id( )
{
   return (this->thread_id);
}

int helium_thread::create_thread(pthread_t *thread_ptr, const pthread_attr_t *attr, void * (*start_routine)(void *), void *arg )
{
   int ret;
   ret = pthread_create(thread_ptr,attr,start_routine,(void *)arg)  ;
   cout<<"Thread created "<<std::hex<<thread_ptr<<endl;
   return ret;

}

helium_thread::helium_thread()
{

    thread_id = new pthread_t;
    cout<<"Constructor called "<<std::hex<<thread_id<<endl;
}

helium_thread::~helium_thread()
{
    cout<<"Destructor called"<<std::hex<<thread_id<<endl;
    delete thread_id;
}


/** While defining the methods of the class, Keywords static and virtual should not be repeated in the definition. **/
/** They should only be used in the class declaration. **/

void handler(int sig)
{
    //do nothing
    cout<<"Handler called"<<endl;
}


void *Thread_Function_A(void *thread_arg)
{
  int rc = 0;

  pid_A = getpid();

  cout<<"The pid value of Thread A is"<< pid_A << endl;

  while(1)
  {

    pthread_mutex_lock(&(helium_thread::mutex_thread));
    cout<<"Thread A lock acquire first"<<endl;

   if ( shared_variable  != 5) 
   {
        /** Now you put a sleep to introduce a race condition **/
        /** You will find that there is no race condition here **/
       cout<<"Going to conditional wait"<<endl;
       //cout<<"Sleep thread A"<<endl;
       //sleep(5);   
       pthread_cond_wait(&helium_thread::cond_var, &helium_thread::mutex_thread);
       cout<<"Sleep after cond_wait"<<endl;
       sleep(5);
       cout<<"The cond_wait is unblocked now "<<endl;
       cout<<"The thread A proceeds"<<endl;
       cout<<"The shared_variable value = "<< std::dec<< shared_variable << endl;

       pthread_mutex_unlock(&(helium_thread::mutex_thread));
       cout<<"Thread A unlocked"<<endl;



   }  
   else
   {  
      cout<<"Else condition thread A..shared variable value is "<<shared_variable<<endl;
      cout<<"The condition of thread A is met now"<<endl;
      pthread_mutex_unlock(&(helium_thread::mutex_thread));
      cout<<"Thread A unlocked in else condition"<<endl;
      pthread_exit(NULL);   
   }

 }
}


void *Thread_Function_B(void *thread_arg)
{
  pthread_mutex_lock(&(helium_thread::mutex_thread));   

  pid_B = getpid();

  cout<<"The pid value of Thread B is"<< pid_B << endl;

  shared_variable = 5;
  /** Now you put a sleep to introduce a race condition **/
  /** You will find that there is no race condition here **/
  //sleep(5);

  cout<<"Signal the thread A now "<<endl;

  pthread_cond_signal (&helium_thread::cond_var);

  cout<<"Changed the shared_variable value now"<<endl;



  pthread_mutex_unlock(&(helium_thread::mutex_thread));

  cout<<"Return thread function b now"<<endl; 

}


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

   pid_t thread_pid_val = getpid();
   thread_1.create_thread((thread_1.get_thread_id()),NULL,Thread_Function_A,&thread_pid_val);
   thread_2.create_thread((thread_2.get_thread_id()),NULL,Thread_Function_B,&thread_pid_val);
   pthread_join( *(thread_1.get_thread_id()), NULL);
   pthread_join( *(thread_2.get_thread_id()), NULL);

   return  0;   
}

Вывод следующий.

    $ ./thread_basic.out 
Constructor called 0x2012010
Constructor called 0x2012030
Thread created 0x2012010
The pid value of Thread A is5bfd
Thread created 0x2012030
Thread A lock acquire first
Going to conditional wait
The pid value of Thread B is5bfd
Signal the thread A now 
Changed the shared_variable value now
Return thread function b now
Sleep after cond_wait
The cond_wait is unblocked now 
The thread A proceeds
The shared_variable value = 5
Thread A unlocked
Thread A lock acquire first
Else condition thread A..shared variable value is 5
The condition of thread A is met now
Thread A unlocked in else condition
Destructor called0x2012030
Destructor called0x2012010
  • 3
    Это слишком много кода. Где твой тест ?
  • 3
    Также, как жесткое правило: правильность вашей программы не должна зависеть от скорости процессора (то есть: наличие или отсутствие вызовов в спящий режим). Это также верно для тестирования - в промышленности у нас есть слово для тестовых случаев, которые включают сны: «хлопьевидный». Предпочитаю что-то коллбэк, управляемый для тестирования или использования макета (gmock google)
Показать ещё 4 комментария
Теги:
multithreading
pthreads

2 ответа

1
Лучший ответ

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

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

using namespace std;

int shared_variable = 7;
pid_t pid_A;
pid_t pid_B;

class helium_thread
{
    private:
        pthread_t *thread_id;
        pid_t process_pid;

    public:
        static pthread_mutex_t mutex_thread;
        static pthread_cond_t cond_var;
        void set_thread_id(pthread_t tid);
        pthread_t *get_thread_id();
        int create_thread(pthread_t *thread_ptr, const pthread_attr_t *attr,
                          void * (*start_routine)(void *), void *arg );
        helium_thread();
        ~helium_thread();
};

helium_thread thread_1, thread_2;

pthread_mutex_t helium_thread::mutex_thread = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t helium_thread::cond_var = PTHREAD_COND_INITIALIZER;

void helium_thread::set_thread_id( pthread_t tid) {*(this->thread_id) = tid;}

pthread_t * helium_thread::get_thread_id() {return (this->thread_id);}

int helium_thread::create_thread(pthread_t *thread_ptr, const pthread_attr_t *attr, void * (*start_routine)(void *), void *arg )
{
    int ret;
    ret = pthread_create(thread_ptr, attr, start_routine, (void *)arg) ;
    return ret;
}

helium_thread::helium_thread()  {thread_id = new pthread_t; }    
helium_thread::~helium_thread() {delete thread_id;}

void *Thread_Function_A(void *thread_arg)
{
    while (1)
    {
        pthread_mutex_lock(&(helium_thread::mutex_thread));
        cout << "TA lock acquired" << endl;

        if ( shared_variable != 5)
        {
            cout << "TA Going to conditional wait" << endl;
            pthread_cond_wait(&helium_thread::cond_var, &helium_thread::mutex_thread);
            cout << "TA Sleep after cond_wait" << endl;
            sleep(5);
            cout << "TA The cond_wait is unblocked now " << endl;
            cout << "TA The thread A proceeds" << endl;
            cout << "TA The shared_variable value = " << shared_variable << endl;
            pthread_mutex_unlock(&(helium_thread::mutex_thread));
            cout << "TA unlocked" << endl;
        }
        else
        {
            cout << "TA Else condition thread A..shared variable value is " << shared_variable << endl;
            cout << "TA The condition of thread A is met now" << endl;
            pthread_mutex_unlock(&(helium_thread::mutex_thread));
            cout << "TA unlocked in else condition" << endl;
            pthread_exit(NULL);
        }
    }
    return NULL;
}

void *Thread_Function_B(void *thread_arg)
{
    pthread_mutex_lock(&(helium_thread::mutex_thread));
    shared_variable = 5;
    cout << "TB Signal the thread A now " << endl;
    pthread_cond_signal (&helium_thread::cond_var);
    cout << "TB the changed shared_variable is now" << endl;
    pthread_mutex_unlock(&(helium_thread::mutex_thread));
    cout << "TB Return thread now" << endl;

    return NULL;
}

int main(int argc, char *argv[])
{
    thread_1.create_thread((thread_1.get_thread_id()), NULL, Thread_Function_A, NULL);
    thread_2.create_thread((thread_2.get_thread_id()), NULL, Thread_Function_B, NULL);
    pthread_join( *(thread_1.get_thread_id()), NULL);
    pthread_join( *(thread_2.get_thread_id()), NULL);

    return 0;
}

Вот три сценария, которые я продолжаю видеть. Ни один из них не содержит код перехода. sleep может (или не может) вводить некоторую изменчивость в исполнении, но я не вижу ничего неожиданного.

Один

TA lock acquired
TA Going to conditional wait
TB Signal the thread A now 
TB the changed shared_variable is now
TA Sleep after cond_wait
TB Return thread now
TA The cond_wait is unblocked now 
TA The thread A proceeds
TA The shared_variable value = 5
TA unlocked
TA lock acquired
TA Else condition thread A..shared variable value is 5
TA The condition of thread A is met now
TA unlocked in else condition

Два

TA lock acquired
TA Going to conditional wait
TB Signal the thread A now 
TB the changed shared_variable is now
TB Return thread now
TA Sleep after cond_wait
TA The cond_wait is unblocked now 
TA The thread A proceeds
TA The shared_variable value = 5
TA unlocked
TA lock acquired
TA Else condition thread A..shared variable value is 5
TA The condition of thread A is met now
TA unlocked in else condition

Три

TB Signal the thread A now 
TB the changed shared_variable is now
TB Return thread now
TA lock acquired
TA Else condition thread A..shared variable value is 5
TA The condition of thread A is met now
TA unlocked in else condition
  • 0
    Просто запустите мой код как есть, и вы увидите разницу. Я удалил много отпечатков, я обнаружил, что он ведет себя так, как вы показали. Тем не менее, просто запустите мою программу как есть. Вы увидите разницу.
  • 0
    Спасибо! Я думаю, я понял. Один вопрос - Между этим if (shared_variable! = 5) {/ ** Если я получу сигнал из потока B здесь ** / Это до pthread_cond_wait ** /. Будет ли мой сигнал потерян. Я вижу, что это не теряется, почему так происходит? pthread_cond_wait (& helium_thread :: cond_var, & helium_thread :: mutex_thread);
Показать ещё 2 комментария
1

Существует разница в двух функциях, выполняемых в потоках Thread_Function_A, Thread_Function_B.

В Thread_Function_B первая вещь, которую вы делаете, - это блокировка, тогда как в Thread_Function_A вы получаете 1pid, затем запускаете цикл while, а затем получаете блокировку.

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

Когда B выпускает блокировку A, она обнаруживает, что общая переменная равна 5 и, следовательно, переходит в другое состояние.

В идеале вы бы хотели, чтобы A до 1-го раза запустил блокировку и подождал, но в вашей текущей реализации этого не происходит.

Попробуйте и 1-й получить блокировку в A, а затем продолжить.

  void *Thread_Function_A(void *thread_arg)
  {
    pthread_mutex_lock(&(helium_thread::mutex_thread));
    //remaining logic
  • 0
    Я отредактировал код, и вы обнаружите, что на самом деле приходит отладочная печать перед условным ожиданием. Это означает, что он находится в состоянии if. Кроме того, если я удаляю сон, эта проблема не возникает. Я пытаюсь понять, почему сон имеет значение здесь.
  • 0
    Вы видели это два раза - Тема A блокировка получает сначала

Ещё вопросы

Сообщество Overcoder
Наверх
Меню