Linux互斥锁线程同步

在学习之前,一直以为同步就是保持工作步调一致。其实不然,线程同步的任务是对多个线程在执行上进行协调,使线程按照一定的规则共享资源,并能很好地相互合作。

互斥锁是线程同步最常见的技术手段,它也被称为互斥量,实质上它就是一个pthread_mutex_t类型的变量。相关的使用函数如下:

(1)初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

(2)销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);

(3)尝试加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);
mutex没被锁上时,当前线程会将mutex锁上,返回0
mutex已被锁上时,失败返回(返回错误号,用strerror()函数打印错误信息),线程不阻塞。

(4)加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
mutex没被锁上时,当前线程会将mutex锁上
mutex已被锁上时,线程会一直阻塞在此位置,直到锁被(其它线程)解开才解除阻塞。

(5)解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);

需要注意:使用互斥锁实现线程同步时,所有的线程在访问共享资源前都需要加锁操作。代码实例:

#include <pthread.h>

//Defining a mutex 
pthread_mutex_t mutex;

void* fooA(void* n)
{
    pthread_mutex_lock(&mutex);     //lock

    //Operating shared resources 
    //...

    pthread_mutex_unlock(&mutex);   //unlock
    return NULL;
}

void* fooB(void* n)
{
    pthread_mutex_lock(&mutex);     //lock

    //Operating shared resources 
    //...

    pthread_mutex_unlock(&mutex);   //unlock
    return NULL;
}


int main(void)
{
    pthread_t p[2];

    //Initialization mutex 
    pthread_mutex_init(&mutex, NULL);

    pthread_create(&p[0], NULL, fooA, NULL);
    pthread_create(&p[1], NULL, fooB, NULL);

    pthread_join(p[0], NULL);
    pthread_join(p[1], NULL);

    //Destroy mutex 
    pthread_mutex_destroy(&mutex);

    return 0;
}

多线程执行理论上是并发执行的,但是加锁后访问共享资源时则降级为串行的,显然这对效率有一定的影响。 另外,使用互斥锁实现线程同步,最典型的软件BUG就是死锁。产生死锁现象后,相关的线程不退出也不工作。造成死锁的原因大致有: 

  • 自己锁自己
void* fooA(void* n)
{
    pthread_mutex_lock(&mutex);     //lock
    pthread_mutex_lock(&mutex);     //lock again,造成死锁现象

    //Operating shared resources 
    //...

    pthread_mutex_unlock(&mutex);   //unlock
    return NULL;
}

类似的,当线程a执行函数上锁后没有解锁就退出,线程b又要企图上锁时同样造成死锁。

如上图,翻译成代码实现为:

pthread_mutex_t mutexA;
pthread_mutex_t mutexB;

resources_t A;
resources_t B;

void* foo1(void* n)
{
    pthread_mutex_lock(&mutexA);    
    //Operating shared resources A
    //...

    pthread_mutex_lock(&mutexB);        //这里发生死锁
    //Operating shared resources B
    //...

    pthread_mutex_unlock(&mutexB);
    pthread_mutex_unlock(&mutexA);

    return NULL;
}

void* foo2(void* n)
{
    pthread_mutex_lock(&mutexB);    
    //Operating shared resources B
    //...

    pthread_mutex_lock(&mutexA);        //这里发生死锁
    //Operating shared resources A
    //...

    pthread_mutex_unlock(&mutexA);
    pthread_mutex_unlock(&mutexB);

    return NULL;
}

线程1对共享资源A加锁成功,获得A锁,线程2对共享资源B加锁成功,获得B锁; 
线程1访问共享资源B,对B加锁,线程1阻塞在等待B锁上,线程2访问共享资源A,对A加锁,线程2阻塞在等待A锁上。

解决办法是: 
(1)让线程按照一定的顺序访问共享资源; 
(2)在访问其他锁之前需要将自己的锁解开; 
(3)使用pthread_mutex_trylock()进行加锁;
 

猜你喜欢

转载自blog.csdn.net/qq_15391889/article/details/86559667