Categories
Uncategorized

Thread Synchronization

table of Contents

  • 1. 线程同步概述

      Thread synchronization definition

      Thread synchronization method

  • 2. 互斥锁

      Mutex concept

    • 互斥锁基本API

        Initialization and Destruction

        Lock and unlock

        Two special lock function

    • Sample Code

  • 3. 避免死锁

      Deadlock concept of threads

      Generates four necessary conditions for deadlock

      How to avoid deadlocks

  • 4. 条件变量

      Condition variable concept

    • 条件变量基本API

        Initialization and Destruction

        Wait condition is met

        Signal to the thread

    • Sample Code

1. Thread Synchronization Overview

Thread synchronization definition

Thread synchronization, refers to the relative order of execution among multiple threads of control, in order to correct and orderly sharing of data between threads, the thread synchronization common usage scenarios.

    Multi-threaded task execution dependencies on order

    Sharing data between threads can simultaneously use a thread

Thread synchronization method

In a real project, thread synchronization method commonly used is divided into three types:

    Mutex

    Condition variable

    Posix semaphore (including known semaphores and unnamed semaphores)

This section describes only condition variables and mutexes, semaphores Posix subsequent presentation in the IPC Posix topic.

2. mutex

Mutex concept

Mutex to ensure that only one thread at the same time access to shared data, use:

    Lock

    Access to shared data

    Unlock

After mutex lock, and any other attempts to lock it again thread will be blocked until the current thread releases the mutex lock, unlock all blocked threads will become runnable, but exactly which run first, which that is uncertain.

Mutex basic API

Initialization and Destruction

Mutex is a type of data represented by pthread_mutex_t, mutex prior to use, requires initialization, initialize two ways:

    Is set to a constant PTHREAD_MUTEX_INITIALIZER, only applies to statically allocated mutex

    Pthread_mutex_init function calls, static allocation and dynamic allocation of mutex can

After you are finished using a mutex, you can call pthread_mutex_destroy destruction, especially for mutex dynamically allocated memory before the release, calling pthread_mutex_destroy is a must.

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

//两个函数的返回值:成功返回0,失败返回错误编号
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

Wherein, the pthread_mutex_init attr second parameter for setting the mutex properties, if the default attributes attr simply set to NULL.

Lock and unlock

//两个函数的返回值:成功返回0,失败返回错误编号
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

Mutex lock, call pthread_mutex_lock, if the mutex is already locked, the calling thread will block until the mutex is released.
    To unlock the mutex, call pthread_mutex_unlock.

Two special lock function

Try to lock

//成功返回0,失败返回错误编号
int pthread_mutex_trylock(pthread_mutex_t *mutex);

If you do not want the calling thread is blocked, you can try using pthread_mutex_trylock locked:

    If the mutex unlocked, pthread_mutex_trylock will lock success, returns 0

    If the mutex is locked, pthread_mutex_trylock will lock fails, the return EBUSY

Limited Time lock

//成功返回0,失败返回错误编号
int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *time);

pthread_mutex_timedlock is a blockage can set the time lock function:

    When the mutex is locked, the calling thread will block set time

    When the set time is reached, pthread_mutex_timedlock unblock the lock fails and returns ETIMEDOUT

On the second argument time, there are two points to note:

    time is the absolute time to wait, it needs to be set to the current time + wait time

    struct timespec time is specified, it is described by second and nanosecond time

Sample Code

/*
 * 测试使用上述4个加锁函数
*/

#include 
#include 
#include 
#include 

pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
pthread_mutex_t mutex3;

void *thread1_start(void *arg)
{
    pthread_mutex_lock(&mutex1);
    printf("thread1 has locked mutex1\n");
    sleep(2); //保证thread2执行时mutex1还未解锁
    pthread_mutex_unlock(&mutex1);
}

void *thread2_start(void *arg)
{
    if (pthread_mutex_trylock(&mutex2) == 0)
        printf("thread2 trylock mutex2 sucess\n");

    if (pthread_mutex_trylock(&mutex1) == EBUSY)
        printf("thread2 trylock mutex1 failed\n");
          
    pthread_mutex_unlock(&mutex2);
}

void *thread3_start(void *arg)
{
    struct timespec time;
    struct tm *tmp_time;
    char s[64];
    int err;
    
    pthread_mutex_lock(&mutex3);
    printf("thread3 has locked mutex3\n");
    
    /*获取当前时间,并转化为本地时间打印*/
    clock_gettime(CLOCK_REALTIME, &time);
    tmp_time = localtime(&time.tv_sec);
    strftime(s, sizeof(s), "%r", tmp_time);
    printf("current time is %s\n", s);
    
    /*设置time = 当前时间 + 等待时间10S*/
    time.tv_sec = time.tv_sec + 10;
    
    /*mutex3已上锁,这里会阻塞*/
    if (pthread_mutex_timedlock(&mutex3, &time) == ETIMEDOUT)
        printf("pthread_mutex_timedlock mutex3 timeout\n");
    
    /*再次获取当前时间,并转化为本地时间打印*/
    clock_gettime(CLOCK_REALTIME, &time);
    tmp_time = localtime(&time.tv_sec);
    strftime(s, sizeof(s), "%r", tmp_time);
    printf("the time is now %s\n", s);  
        
    pthread_mutex_unlock(&mutex3);
}

int main()
{     
    pthread_t tid1;
    pthread_t tid2;
    pthread_t tid3;
    
    /*测试pthread_mutex_lock和pthread_mutex_trylock*/
    pthread_mutex_init(&mutex1, NULL);
    pthread_mutex_init(&mutex2, NULL);
       
    pthread_create(&tid1, NULL, thread1_start, NULL);   
    pthread_create(&tid2, NULL, thread2_start, NULL);
    
    if (pthread_join(tid1, NULL) == 0)
    {
        pthread_mutex_destroy(&mutex1);
    }
    
    if (pthread_join(tid2, NULL) == 0)
    {
        pthread_mutex_destroy(&mutex2);
    }    
    
    /*测试pthread_mutex_timedlock*/
    pthread_mutex_init(&mutex3, NULL);
    pthread_create(&tid3, NULL, thread3_start, NULL);
      
    if (pthread_join(tid3, NULL) == 0)
    {
        pthread_mutex_destroy(&mutex3);
    }      
    
    return 0;
}

3. Avoid Deadlock

Deadlock concept of threads

Deadlock between threads, referring to the critical resources between threads wait for each other and cause each other phenomenon can not continue to perform.

Generates four necessary conditions for deadlock

    Mutually exclusive conditions: resource while a thread can only be used at this time if other threads requesting the resource, the request thread must wait

    Inalienable condition: Resource threads obtained before completion, other threads can not be robbed is not used, can only take the initiative to obtain the release of the resource thread

    Request to maintain conditions: the thread has been at least one resource, but proposed a new resource request, and the new resources have been occupied by other threads, this time requesting thread is blocked, but the resources that they have available to keep hold

    Circular wait condition: There is a resource waiting ring, each thread will occupy at least one resource needed for the next thread

Intuitively, it seems circular wait condition and deadlock as defined, it is not true, because the deadlock in the definition of more stringent requirements:

    Circular wait condition requires P (i + 1) resource requirements, at least one from P (i) to

    Deadlock defined in claim P (i + 1) the required resources, provided only by P (i) and by the

How to avoid deadlocks

    All threads lock in the same order

    To all the critical resource is assigned a unique serial number, the corresponding thread-locking is also assigned the same number, all threads in the system resource requests in strict order of increasing

    Use pthread_mutex_trylock try to lock, if the failure to give up lock, while releasing the lock has occupied

    Use pthread_mutex_timedlock limit lock, if locked overtime to give up, while releasing the lock has occupied

4. Condition Variables

Condition variable concept

    Condition variables are another thread synchronization mechanism available, it provides a place for multi-threaded a round

    Condition variable itself requires, thread must be locked by the mutex protection prior to changing conditions, other threads before acquiring mutex does not know the conditions changed

    Used with condition variables and mutex, the thread can wait in a non-competitive manner specified conditions occur

The basic condition variable API

Initialization and Destruction

Condition variable is represented by a pthread_cond_t data type, and the like mutexes, condition variables initialization method, there are two:

    Condition variable is set to a constant PTHREAD_COND_INITIALIZER, only for static allocation

    Pthread_cond_init condition variable call function for static allocation and dynamic allocation

After the condition variable used up, you can call pthread_cond_destroy destruction, the same, if the condition variable is dynamically allocated, before the release of memory, this operation is necessary.

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

//两个函数的返回值:成功返回0,失败返回错误编号
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);

Wherein, pthread_cond_init attr second parameter for setting a condition variable properties, if the default attributes attr simply set to NULL.

Wait condition is met

//两个函数的返回值:成功返回0,失败返回错误编号
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *timeout);

Call waiting function can pthread_cond_wait conditions are met, use the following steps, passed to the mutex function of the conditions for protection, before the conditions are met, the calling thread will block.

    The calling thread mutex locked passed pthread_cond_wait

    pthread_cond_wait the calling thread is automatically placed on the list of threads waiting for the condition, and then unlock the mutex

    When the condition is satisfied, The pthread_cond_wait return, the mutex is locked again

    After pthread_cond_wait return, then the calling thread to unlock a mutex

pthread_cond_timedwait is a timed wait function conditions are met, and if conditions are not satisfied timeout occurs, pthread_cond_timedwait will re mutex lock, then return ETIMEDOUT error.

Note: When conditions return from pthread_cond_wait and pthread_cond_timedwait, the calling thread must be recalculated condition, because another thread may already be running and changing conditions.

Signal to the thread

There are two functions can be used to inform the threads have been met:

    pthread_cond_signal can at least wake up a thread waiting on the condition

    pthread_cond_broadcast can wake up all threads waiting on the condition

//两个函数的返回值:成功返回0,失败返回错误编号
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

When calling the above two functions, we say that this is the signal to the thread, attention must first acquire a mutex, and then change the conditions, and then send a signal to the thread, and finally to unlock the mutex.

Sample Code

/*
 * 结合使用条件变量和互斥锁进行线程同步
*/

#include 
#include 

static pthread_cond_t  cond;
static pthread_mutex_t mutex;
static int             cond_value;
static int             quit;

void *thread_signal(void *arg)
{
    while (!quit)
    {
        pthread_mutex_lock(&mutex);
        cond_value++;                //改变条件,使条件满足
        pthread_cond_signal(&cond);  //给线程发信号 
        printf("signal send, cond_value: %d\n", cond_value);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }    
}

void *thread_wait(void *arg)
{
    while (!quit)
    {
        pthread_mutex_lock(&mutex);
        
        /*通过while (cond is true)来保证从pthread_cond_wait成功返回时,调用线程会重新检查条件*/
        while (cond_value == 0)
            pthread_cond_wait(&cond, &mutex);
            
        cond_value--;
        printf("signal recv, cond_value: %d\n", cond_value);
        
        pthread_mutex_unlock(&mutex);
        sleep(1);
    } 
}

int main()
{     
    pthread_t tid1;
    pthread_t tid2;
    
    pthread_cond_init(&cond, NULL);
    pthread_mutex_init(&mutex, NULL);
       
    pthread_create(&tid1, NULL, thread_signal, NULL);   
    pthread_create(&tid2, NULL, thread_wait, NULL);
    
    sleep(5);
    quit = 1;
    
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    
    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);
      
    return 0;
}

Leave a Reply