Thread synchronization – mutex unlock, unlock

Similar to the locking and unlocking of inter-process communication semaphores.

After the mutex is locked, any other thread that attempts to lock the mutex here will be blocked until the current thread releases the mutex lock. If multiple threads are blocked when the mutex is released, all threads blocked on the mutex will become runnable. The first thread that becomes runnable can lock the mutex, and other threads can lock the mutex. You will see that the mutex is still locked, and you can only go back and wait for it to become available again. In this way, only one thread can run forward at a time.

At the time of design, it is necessary to stipulate that all threads must comply with the same data access rules. Only in this way can the mutual exclusion mechanism work properly. If one of the threads is allowed to access the shared resource without acquiring the lock, then even if other threads acquire the lock before using the shared resource, data inconsistency will still occur.

Mutex variables are represented by the pthread_mutex_t data type. A mutex variable must be initialized before using it. You can make it a constant.

PTREAD_MUTEX-INITIALIZWE (only for statically allocated mutexes), you can also initialize it by calling the pthread_mutex_init function. If the mutex is allocated dynamically (for example, by calling malloc), pthread_mutex_destory needs to be called before releasing the memory.

Create a mutex lock

Function prototype

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

Parameters

pthread_mutex_t *restrict mutex: the address of the lock.

onst pthread_mutexattr_t *restrict attr: lock attribute, can be NULL, default attribute creates lock

Return value

Returns 0 if successful, otherwise returns an error number

Destroy the mutex lock

Function prototype

int pthread_mutex_destroy(pthread_mutex_t *mutex);

Parameters

pthread_mutex_t *mutex: address of the lock.

Return value

Returns 0 if successful, otherwise returns an error number

Lock, unlock

Function prototype:

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

Return value:

Returns 0 if successful, otherwise returns an error number

If the thread does not want to be blocked, it can use pthread_mutex_trylock to try to lock the mutex. If the mutex is in an unlocked state when pthread_mutex_trylock is called, then pthread_mutex_trylock will lock the mutex without blocking and return 0. Otherwise, pthread_mutex_trylock will fail and cannot lock the mutex and return EBUSY.

Example 1:

In the previous section, a question was raised: how to ensure that the t1 thread runs first. We can use g_data as a mutex and lock and unlock it.

#include <stdio.h>
#include <pthread.h>
//int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
//int pthread_mutex_destroy(pthread_mutex_t *mutex);
//int pthread_mutex_lock(pthread_mutex_t *mutex);
//int pthread_mutex_trylock(pthread_mutex_t *mutex);
//int pthread_mutex_unlock(pthread_mutex_t *mutex);
int g_data=0;

pthread_mutex_t mutex;

void *func1(void *arg)
{
        int i;
        pthread_mutex_lock( & amp;mutex);
        for(i=0;i<3;i + + )
        {
                printf("t1:%ld thread is creart\\
",(unsigned long)pthread_self());
                printf("t1:param is %d\\
",*((int *)arg));
                sleep(1);
        }
        pthread_mutex_unlock( & amp;mutex);
}

void *func2(void *arg)
{
        pthread_mutex_lock( & amp;mutex);
        printf("t2:%ld thread is creart\\
",(unsigned long)pthread_self());
        printf("t2:param is %d\\
",*((int *)arg));
        pthread_mutex_unlock( & amp;mutex);
}

int main()
{
        int param=100;
        char *pret=NULL;
        int ret1;
        int ret2;
        pthread_t t1;
        pthread_t t2;

        pthread_mutex_init( & amp;mutex,NULL);

        ret1=pthread_create( & amp;t1, NULL,func1, (void *) & amp;param);
        ret2=pthread_create( & amp;t1, NULL,func2, (void *) & amp;param);
        if(ret1 == 0)
        {
                printf("main:create t1 successful\\
");
        }

        if(ret2 == 0)
        {
                printf("main:create t2 successful\\
");
        }


        printf("main:%ld\\
",(unsigned long)pthread_self());


        pthread_join(t1,NULL);
        printf("main: t1 quit:%s\\
",pret);
        pthread_join(t2,NULL);
        printf("main: t2 quit:%s\\
",pret);
        pthread_mutex_destroy( & amp;mutex);
        return 0;
}
       

Judging from the results of running the above code, no matter how many times the code is run, the t2 thread will not run until the t1 thread has finished running. The reason why the main function does not run after t1 is that the main thread does not participate in the locking and unlocking of the mutex.

Example 2: Mutex restricts access to shared resources

The previous section also mentioned a problem, how to ensure that the t1 thread exits when g_data=3

#include <stdio.h>
#include <pthread.h>
//int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
//int pthread_mutex_destroy(pthread_mutex_t mutex);
//int pthread_mutex_lock(pthread_mutex_t mutex);
//int pthread_mutex_trylock(pthread_mutex_t mutex);
//int pthread_mutex_unlock(pthread_mutex_t mutex);

int g_data=0;

void *func1(void *arg)
{
        printf("t1:%ld thread is creart\\
",(unsigned long)pthread_self());
        printf("t1:param is %d\\
",*((int *)arg));
        pthread_mutex_lock( & amp;mutex);
        while(1)
        {
                printf("t1:%d\\
",g_data + + );
                sleep(1);

                if(g_data==3)
                {
                        printf("==================\\
");
                        pthread_mutex_unlock( & amp;mutex);
                        pthread_exit(NULL);
                }
        }

}

void *func2(void *arg)
{
        printf("t2:%ld thread is creart\\
",(unsigned long)pthread_self());
        printf("t2:param is %d\\
",*((int *)arg));
        while(1)
        {
                printf("t2:%d\\
",g_data);
                pthread_mutex_lock( & amp;mutex);
                g_data + + ;
                pthread_mutex_unlock( & amp;mutex);
                sleep(1);
        }
}

int main()
{
        int param=100;
        char *pret=NULL;
        int ret1;
        int ret2;
        pthread_t t1;
        pthread_t t2;

        pthread_mutex_init( & amp;mutex,NULL);

        ret1=pthread_create( & amp;t1, NULL,func1, (void *) & amp;param);
        ret2=pthread_create( & amp;t1, NULL,func2, (void *) & amp;param);
        if(ret1 == 0)
        {
                printf("main:create t1 successful\\
");
        }

        if(ret2 == 0)
        {
                printf("main:create t2 successful\\
");
        }


        printf("main:%ld\\
",(unsigned long)pthread_self());


        while(1)
        {

                printf("main:%d\\
",g_data);
                sleep(1);
        }
        pthread_join(t1,NULL);
        pthread_join(t2,NULL);

        pthread_mutex_destroy( & amp;mutex);
        return 0;
}

Because t1 will be run at least once before g_data reaches 3, it can be locked when running t1 until g_data=3, and t1 can be unlocked after exiting. You can see the above Dynamic operation situation. As long as it runs to the t1 thread, it will exit when g_data=3, and then run t2 and main.