Quantcast
Viewing latest article 3
Browse Latest Browse All 3

Understanding pthreads locks and condition variables

I had an exercise about threads, locks, and condition variables in C. I needed to write a program that get data, turn it into a linked list, starting 3 threads each calculating result for each node in the list and main thread printing the results after evreyone finished.

This is the main function:

int thread_finished_count;// Lock and Conditional variablepthread_mutex_t list_lock;pthread_mutex_t thread_lock;pthread_cond_t thread_cv;int main(int argc, char const *argv[]){    node *list;    int pairs_count, status;    thread_finished_count = 0;    /* get the data and start the threads */    node *head = create_numbers(argc, argv, &pairs_count);    list = head; // backup head for results    pthread_t *threads = start_threads(&list);    /* wait for threads and destroy lock */    status = pthread_cond_wait(&thread_cv, &list_lock);    chcek_status(status);    status = pthread_mutex_destroy(&list_lock);    chcek_status(status);    status = pthread_mutex_destroy(&thread_lock);    chcek_status(status);    /* print result in original list */    print_results(head);    /* cleanup */    wait_for_threads(threads, NUM_THREADS);    free_list(head);    free(threads);    return EXIT_SUCCESS;}

Please note that the create_numbers function is working properly, and the list is working as intended.

Here is the start_thread and thread_function code:

pthread_t *start_threads(node **list){    int status;    pthread_t *threads = (pthread_t *)malloc(sizeof(pthread_t) * NUM_THREADS);    check_malloc(threads);    for (int i = 0; i < NUM_THREADS; i++)    {        status = pthread_create(&threads[i], NULL, thread_function, list);        chcek_status(status);    }    return threads;}void *thread_function(node **list){    int status, self_id = pthread_self();    printf("im in %u\n", self_id);    node *currentNode;    while (1)    {        if (!(*list))            break;        status = pthread_mutex_lock(&list_lock);        chcek_status(status);        printf("list location %p thread %u\n", *list, self_id);        if (!(*list))        {            status = pthread_mutex_unlock(&list_lock);            chcek_status(status);            break;        }        currentNode = (*list);        (*list) = (*list)->next;        status = pthread_mutex_unlock(&list_lock);        chcek_status(status);        currentNode->gcd = gcd(currentNode->num1, currentNode->num2);        status = usleep(10);        chcek_status(status);    }    status = pthread_mutex_lock(&thread_lock);    chcek_status(status);    thread_finished_count++;    status = pthread_mutex_unlock(&thread_lock);    chcek_status(status);    if (thread_finished_count != 3)        return NULL;    status = pthread_cond_signal(&thread_cv);    chcek_status(status);    return NULL;}void chcek_status(int status){    if (status != 0)    {        fputs("pthread_function() error\n", stderr);        exit(EXIT_FAILURE);    }}

Note that self_id is used for debugging purposes.

My problem

  1. My main problem is about splitting the work. Each thread so get an element from the global linked list, calculate gcd, then go on and take the next element. I get this effect only if I'm adding usleep(10) after I unlock the mutex in the while loop. If I don't add the usleep, the FIRST thread will go in and do all the work while other thread just wait and come in after all the work has been done.

Please note!: I thought about the option that maybe the first thread is created, and untill the second thread is created the first one already finish all the jobs. This is why I added the "I'm in #threadID" check with usleep(10) when evrey thread is created. They all come in but only first one is doing all the jobs.Here is the output example if I do usleep after the mutex unlock (notice diffrent thread ID)

with usleep

./v2 nums.txtim in 1333593856list location 0x7fffc4fb56a0 thread 1333593856im in 1316685568im in 1325139712list location 0x7fffc4fb56c0 thread 1333593856list location 0x7fffc4fb56e0 thread 1316685568list location 0x7fffc4fb5700 thread 1325139712list location 0x7fffc4fb5720 thread 1333593856list location 0x7fffc4fb5740 thread 1316685568list location 0x7fffc4fb5760 thread 1325139712list location 0x7fffc4fb5780 thread 1333593856list location 0x7fffc4fb57a0 thread 1316685568list location 0x7fffc4fb57c0 thread 1325139712list location 0x7fffc4fb57e0 thread 1333593856list location 0x7fffc4fb5800 thread 1316685568list location (nil) thread 1325139712list location (nil) thread 1333593856...normal result output...

And thats the output if I comment out the usleep after mutex lock (Notice the same thread ID)without usleep

  ./v2 nums.txtim in 2631730944list location 0x7fffe5b946a0 thread 2631730944list location 0x7fffe5b946c0 thread 2631730944list location 0x7fffe5b946e0 thread 2631730944list location 0x7fffe5b94700 thread 2631730944list location 0x7fffe5b94720 thread 2631730944list location 0x7fffe5b94740 thread 2631730944list location 0x7fffe5b94760 thread 2631730944list location 0x7fffe5b94780 thread 2631730944list location 0x7fffe5b947a0 thread 2631730944list location 0x7fffe5b947c0 thread 2631730944list location 0x7fffe5b947e0 thread 2631730944list location 0x7fffe5b94800 thread 2631730944im in 2623276800im in 2614822656...normal result output...
  1. My second question is about order of threads working. My exercise ask me to not use join to synchronize the threads (only use at the end to "free resources") but instand use that condition variable.

My goal is that each thread will take element, do the calculation and in the meanwhile another thread will go in and take another element, and new thread will take each element (or at least close to that)

Thanks for reading and I appriciate your help.


Viewing latest article 3
Browse Latest Browse All 3

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>