14

Using trylock:

FILE           *fp;
pthread_mutex_t demoMutex;

void * printHello (void* threadId)
{
    pthread_mutex_trylock (&demoMutex);

    pthread_t      writeToFile = pthread_self ();
    unsigned short iterate;
    for (iterate = 0; iterate < 10000; iterate++)
    {
        fprintf (fp, " %d ",  iterate,         4);
        fprintf (fp, " %lu ", writeToFile, sizeof (pthread_t));
        fprintf (fp, "\n",     writeToFile, 1);
    }

    pthread_mutex_unlock (&demoMutex);
    pthread_exit (NULL);
}

and then main ():

int main ()
{
    pthread_t        arrayOfThreadId [5];
    int                  returnValue;
    unsigned int iterate;

    fp = fopen ("xyz", "w");
    pthread_mutex_init (&demoMutex, NULL);

    for (iterate = 0; iterate < 5; iterate++)
    {
        if (returnValue = pthread_create (&arrayOfThreadId [iterate],
                                    NULL,
                                    printHello,
                                    (void*) &arrayOfThreadId [iterate]) != 0)
        {
            printf ("\nerror: pthread_create failed with error number %d", returnValue);
        }
    }

    for (iterate = 0; iterate < 5; iterate++)
        pthread_join (arrayOfThreadId [iterate], NULL);

    return 0;
}

Here the output first prints some of the first thread and then the rest, and then again the first. The lock isn't working. If I replace the same with pthread_mutex_lock every thing gets shown very sequentially!

What's the ridiculous mistake here?

idmean
  • 14,540
  • 9
  • 54
  • 83
Aquarius_Girl
  • 21,790
  • 65
  • 230
  • 411

6 Answers6

32

It does not make sense to call pthread_mutex_trylock() without testing the result.

If it fails to acquire the mutex, you should not enter the critical section, and you should not unlock it later. For example, you could rewrite it like so (note that you are also very confused about how fprintf() should be called):

void *printHello(void *threadId)
{
    if (pthread_mutex_trylock(&demoMutex) == 0)
    {
        unsigned short iterate;
        for (iterate = 0; iterate < 10000; iterate++)
        {
            fprintf (fp, " %d\n", iterate);
        }

        pthread_mutex_unlock (&demoMutex);
    }

    pthread_exit (NULL);
}

However, it probably makes more sense to use pthread_mutex_lock() instead of pthread_mutex_trylock(), so that your thread will wait for the mutex to be available if it is contended. pthread_mutex_lock() is in almost all cases what you want; the _trylock variant is only for optimising some unusual cases - if you ever encounter a situation where _trylock is needed, you'll know.

caf
  • 233,326
  • 40
  • 323
  • 462
  • Out of curiosity, can you provide an example where `..._lock()` is preferred to `..._trylock()`? Ex. if you want to implement a timeout, wouldn't `trylock` be the choice? Or does it not make sense to do a timeout? – theMayer Feb 15 '18 at 22:22
  • 1
    `..._lock()` is the appropriate option in most cases because in most cases, the thread cannot make any useful forward progress without taking the mutex. In this case, if a `...trylock()` is used and cannot take the lock, the only option is to loop around and attempt to lock it again. Timeouts should only be required in cases where an external event is required for forward progress, which tends to map to a condition variable and hence `pthread_cond_timedwait()` can be used. An unbounded wait on a mutex indicates a problem in the code itself rather than an exceptional condition. – caf Feb 19 '18 at 00:35
  • Agree with the last sentence there... I've got some legacy code where the developer got a little bit too ambitious and maybe didn't understand how threading works. The code also is attempting to sleep the thread for a single millisecond at a time. You have to laugh, otherwise it makes you cry! – theMayer Feb 19 '18 at 01:03
7
...
while (pthread_mutex_trylock(&demoMutex) == 0)
...

Your code makes no sense. Where is it force locked? It's like a not working spinlock that use more CPU?!

trylock returns 0 when it locks, so:

if(!pthread_mutex_trylock(&demoMutex))
{
  // mutex locked
}

The pthread_mutex_trylock() function shall return zero if a lock on the mutex object referenced by mutex is acquired. Otherwise, an error number is returned to indicate the error.

idmean
  • 14,540
  • 9
  • 54
  • 83
Gesior.pl
  • 106
  • 1
  • 2
7

caf had a great answer on how to use it. I just had to grab that explanation for myself, however I did learn that pthread_mutex_lock() has far more overhead in class and just tested it out using the <time.h> lib and the performance for my loop was significantly increased. Just adding in that two cents since he mentioned that maybe you should use pthread_mutex_lock() instead!

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define NUM_THREADS 4
#define LOOPS 100000

int counter;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// using pthread_mutex_lock
void* worker() {
    for (int i = 0; i < LOOPS; i++) {
        pthread_mutex_lock(&mutex);
        counter++;
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit(NULL);
}

// using try_lock - obviously only use one at a time
void* worker() {
    for (int i = 0; i < LOOPS; i++) {
        while (pthread_mutex_trylock(&mutex) != 0) {
            // wait - treated as spin lock in this example
        } 

        counter++;
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit(NULL);
}

int main(int argc, char *argv[]) {
    clock_t begin = clock();
    pthread_t t[NUM_THREADS];
    int rc;

    counter = 0;

    for (int i = 0; i < NUM_THREADS; i++) {
        rc = pthread_create(&t[i], NULL, worker, NULL);

        if (rc) {
            printf("Thread #%d failed\n", i);
        }
    }

    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(t[i], NULL);
    }

    printf("%d\n", counter);
    clock_t end = clock();
    double time = (double)(end - begin) / CLOCKS_PER_SEC;

    printf("Time Spent: %f", time);

    return 0;   
}

Obviously you would comment out one worker to test it, but if you try it out, I get Time Spent: 1.36200 as an average for pthread_mutex_lock() and Time Spent: 0.36714 for pthread_mutex_trylock().

Goes faster again if you use Atomics.

Dennis O'Keeffe
  • 506
  • 4
  • 7
  • This answer deserve an up vote. For certain situations, you do need to use _trylock, but make sure you check the result result of the result. – Ryan X Jul 16 '17 at 17:22
  • I wonder if the spin lock in this case could be the cause of your performance increase? See https://stackoverflow.com/questions/6603404/when-is-pthread-spin-lock-the-right-thing-to-use-over-e-g-a-pthread-mutex – theMayer Feb 15 '18 at 22:29
  • Well it looks dangerous to me. Imagine mutex is not available in that case ur thread will use 100% CPU looping, instead of waiting like in case if mutex_lock – Boris Ivanov Feb 05 '19 at 13:09
2

a modified version of force locked with while loop should be more stable.

void *printHello(void *threadId)

{

   while (pthread_mutex_trylock(&demoMutex) == 0)

   {

        unsigned short iterate;

        for (iterate = 0; iterate < 10000; iterate++)

        {

            fprintf (fp, " %d\n", iterate);

        }


        pthread_mutex_unlock (&demoMutex);

        break;

    }

pthread_exit (NULL);

}`

lyxmoo
  • 21
  • 2
2

The use of pthread_mutex_trylock is used to ensure that tou will not cause a race to a specific command. In order to do so, you must use pthread_mutex_trylock as a condition! an not assume that it would work by it self. example- while(pthread_mutex_trylock(&my_mutex)==0){ printf("The mutex is in my control!!\n"); }

that way you can be sure that even if the mutex is now locked, you are doing abusy-waiting for it in that particular thread.

Ron Cohen
  • 23
  • 5
2

The code is meant to block to ensure mutual exclusion where you call pthread_mutex_trylock(). Otherwise it is undefined behavior. Therfore you must call pthread_mutex_lock().

yves Baumes
  • 8,836
  • 7
  • 45
  • 74