Get stuck executing multithreads (pthread on Windows)
Asked Answered
T

2

1

I hope you will be able to help me in my trouble. My program is doing something I don't really understand. The program purpose is given as below: Create two threads (task_timer and task_read). These threads have to display on the standard output (stdout) the following message : "Tache 1 Tache 2 Tache 1 Tache 2 ..."

Code :

static void* task_timer(void* arg);
static void* task_read(void* p);
struct strShared{
    pthread_mutex_t mut;
    pthread_mutex_t mut2;
    pthread_cond_t synchro;
    pthread_cond_t synchro2;
};


struct strTimer{
    int wt;
    strShared* psh;
};

static void* task_timer(void* p){
    time_t echeance;
    strTimer* timer;
    int time_waiting = 1000;
    time_t  now;
    if(p != NULL){
        timer = p;
        time_waiting = timer->wt; //ms
        echeance = time (NULL) + TIME_OF_THREAD;

        while (1)
        {
            pthread_mutex_lock(&timer->psh->mut);
            printf("Tache 1\n");
            pthread_cond_signal(&timer->psh->synchro);
            pthread_cond_wait(&timer->psh->synchro2, &timer->psh->mut);
            pthread_mutex_unlock(&timer->psh->mut);
        }
    }
    return NULL;
}

static void* task_read(void* p){
    strTimer* timer;
    if(p != NULL){
        timer = p;
        while(1){
            pthread_mutex_lock(&timer->psh->mut);
            pthread_cond_wait(&timer->psh->synchro, &timer->psh->mut);
            printf("Tache 2\n");
            pthread_cond_signal(&timer->psh->synchro2);
            pthread_mutex_unlock(&timer->psh->mut);
        }

    }
    return NULL;
}
int main (void)
{

    pthread_t ttimer;
    pthread_t tread;

    /* TIMER */
    strTimer timer;
    strShared shtimer;
    shtimer.mut = PTHREAD_MUTEX_INITIALIZER;
    shtimer.mut2 = PTHREAD_MUTEX_INITIALIZER;
    shtimer.synchro = PTHREAD_COND_INITIALIZER;
    shtimer.synchro2 = PTHREAD_COND_INITIALIZER;
    timer.psh = &shtimer;
    timer.wt = 1000;

    /* Threads */
    pthread_create(&ttimer, NULL, task_timer, &timer);
    pthread_create(&tread, NULL, task_read, &timer);

   pthread_join(ttimer,NULL);
   pthread_join(tread,NULL);
   return 0;
}

According to me, this code is the good way to achieve this aim. However, it is not working then I guess I did some mistakes. According to me it is working as below:

  1. Both threads are created and executed in paralelism
  2. Task_read take the mutex mut, wait for the signal synchro and free the mutex because the signal is never arrived
  3. Task_timer take the mutex mut and display "Tache 1" on the standard output
  4. Then, Task_timer sends the signal synchro and waits for the signal synchro2 (free the mutex because the signal is never arrived)
  5. Task_read receives the signal synchro and take the mutex mut and displays "Tache 2"
  6. Task_read sends the signal synchro2 and free the mutex mut and go to the begining of the While loop
  7. Task_timer receives the signal synchro2 and free the mutex and go to the begining of the While loop

However, this is not happened like that. Actually, it seems the program gets stuck after displaying "Tache 1". Someone could explain me why this happens please ? I guess I think bad but I would like to understand ...

Many thanks, esc39

Temperance answered 12/6, 2017 at 14:33 Comment(4)
First of all: why have you assumed task_read would acquire mutex first? If two threads contest lock, you cannot predict which one will be granted the lock. From programmer's point of view you can consider it as random. So, please analyse what happens in case the task_timer will acquire the lock first.Verbose
Hi thanks for you answer. Yes I assumed this case but I know there is another case. Whatever the case, the pthread_cond_wait will impose task_read to wait for the signal.Temperance
No, it won't. Please edit your question and do the analysis for the case task_timer get the lock first.Verbose
Hint: signals are not queued. If signal is sent when no one waits for it, it gets lost.Verbose
S
0

If you are new to multi threading I suggest not using condition variables. You do not need to use any condition variable for the objective you described. So remove pthread_cond_wait and pthread_cond_signal lines in both threads.

Instead you can simply add the sleep function after unlocking the mutex in each thread. For e.g. task_read can be modified to:

pthread_mutex_lock(&timer->psh->mut);    
printf("Tache 2\n");
pthread_mutex_unlock(&timer->psh->mut);
usleep(10000);
Snodgrass answered 12/6, 2017 at 15:38 Comment(0)
V
0

Let's see what happens when task_timer acquire lock first.

pthread_mutex_lock(&timer->psh->mut);      /*acquires a lock*/                                     /*|*/
printf("Tache 1\n");                       /*output*/                                              /*|*/
pthread_cond_signal(&timer->psh->synchro); /*sends a signal*/                                      /*|*/ /* no one is waiting for the signal on this cond, so the signal is ignored*/
pthread_cond_wait(&timer->psh->synchro2, &timer->psh->mut); /*releases lock and waits for signal*/ /*|*/ pthread_mutex_lock(&timer->psh->mut); /*acquires a lock*/
                                                                                                   /*|*/ pthread_cond_wait(&timer->psh->synchro, &timer->psh->mut); /*releases lock and waits for signal*/
                                                                                                   /*|*/
                                                                                                   /*|*/
                                                                                                   /*|*/
                                                                                                   /*|*/ printf("Tache 2\n"); /*never happens*/
pthread_mutex_unlock(&timer->psh->mut);    /*never happens*/                                       /*|*/ pthread_cond_signal(&timer->psh->synchro2);
                                                                                                   /*|*/ pthread_mutex_unlock(&timer->psh->mut);

Deadlock.

Simple recipe: put your pthread_cond_signal() calls outside the critical sections. Consider that as a rule of thumb. When you have a couple or more of threads synchronising with the same set of mutex/cond I can hardly imagine a scenario when it is reasonable to signal from critical section. Semantic of signal and broadcast is like: hey, guys, I'm done with my work on the critical resources, you may follow right on. When the thread is inside critical section, by signalling the cond it makes a false statement. Because it is not done.

BTW, in your case you need an additional flag telling which thread should run. And call the pthread_cond_wait() only in case the flag tells is the other thread's turn.

So, the basic algorithm for each thread would be (in pseudocode):

while(loop_again) {
  do_processing_on_non_critical_resources(); /* it's optional */
  lock(mutex);
  while(not_my_turn) { /* explained later */
    wait(cond,mutex);
  }
  do_processsing_on_critical_resources();
  set_flag_to_other_thread();
  unlock(mutex);
  signal(cond);
  do_processing_on_non_critical_resources(); /* it's optional */
}

The check for not_my_turn is in while loop instead of simple if check, because, according to the documentation, there could be spurious wakeup from the pthread_cond_timedwait() or pthread_cond_wait():

When using condition variables there is always a Boolean predicate involving shared variables associated with each condition wait that is true if the thread should proceed. Spurious wakeups from the pthread_cond_timedwait() or pthread_cond_wait() functions may occur. Since the return from pthread_cond_timedwait() or pthread_cond_wait() does not imply anything about the value of this predicate, the predicate should be re-evaluated upon such return.

So, above you have a general case of synchronised thread. However for your case the answer from M.KHd is correct and sufficient.

Verbose answered 13/6, 2017 at 14:22 Comment(1)
Man page: "The pthread_cond_broadcast() or pthread_cond_signal() functions may be called by a thread whether or not it currently owns the mutex that threads calling pthread_cond_wait() or pthread_cond_timedwait() have associated with the condition variable during their waits; however, if predictable scheduling behavior is required, then that mutex shall be locked by the thread calling pthread_cond_broadcast() or pthread_cond_signal()."Gastritis

© 2022 - 2024 — McMap. All rights reserved.