How to use pthread_mutex_trylock?

2019-04-05 18:57发布

问题:

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?

回答1:

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.



回答2:

...
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.



回答3:

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.



回答4:

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().



回答5:

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);

}`



回答6:

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.



标签: c pthreads mutex