linux线程同步:互斥锁

互斥锁(互斥量)是线程用来同步彼此行为的工具。互斥锁可以帮助线程同步对共享资源的使用,以防如下情况发生:线程某甲试图访问一共享变量时,线程某乙正在对其修改。

未避免线程更新共享变量时所出现的问题,必须使用互斥锁来确保同时仅有一个线程可以访问某项共享资源。

互斥锁有两种状态:已锁定(locked)和未锁定(unlocked)。任何时候,至多只有一个线程可以锁定该互斥锁。试图对已经锁定的某一互斥量再次加锁,将可能阻塞线程或者报错失败,具体取决于加锁时使用的方法。一旦线程锁定互斥锁,随机成为该互斥锁的所有者,只有所有者才能给互斥锁解锁。


互斥锁初始化:

互斥锁是属于pthread_mutex_t类型的变量,使用之前必须对其初始化。

初始化有两种方式:一种是静态赋值法,另一种是动态初始化。

静态赋值法只适用于静态分配的互斥量如静态变量和全局变量定义的方式,且只能赋值普通锁

赋值形式:pthread_mutex_t mutex= PTHREAD_MUTEX_INITIALIZER

PTHREAD_MUTEX_INITIALIZER是指将互斥锁初始化为普通锁。锁的不同种类在下面会讲到。


动态初始化互斥锁是通过pthread_mutex_init函数初始化互斥锁,该函数原型如下:

int pthread_mutex_init(pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);

动态初始化互斥锁适用于以下情况:

1.动态分配于堆中的互斥锁。

2.互斥锁是在栈中分配的自动变量。

3.初始化经由静态分配,但不使用默认属性的普通锁

以上情况只能用动态初始化函数初始化互斥锁。


函数中的参数mutexattr表示互斥锁的属性,如果为NULL则使用默认属性(普通锁)。互斥锁的属性及意义如下:

* PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
* PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
* PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
* PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。



初始化以后,就可以对互斥锁进行加锁和解锁操作了。

锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。

加锁:

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

用pthread_mutex_lock()加锁时,如果mutex已经被锁住,当前尝试加锁的线程就会阻塞,直到互斥锁被其他线程释放。
pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

解锁:

int pthread_mutex_unlock(pthread_mutex_t *mutex)

解锁时要满足两个条件:

一是互斥锁必须处于加锁状态,二是调用本函数的线程必须是给互斥锁加锁的进程。


摧毁互斥锁:

当不再需要经由自动或动态分配的互斥锁时,应使用pthread_mutex_destory()将其摧毁。(对于使用PTHREAD_MUTEX_INITIALIZER静态初始化的互斥锁,无需调用pthread_mutex_destory())。

#include<pthread.h>

int pthread_mutex_destory(pthreead_mutex_t* mutex);


以下是一个利用互斥锁的一个例子:

#include <stdio.h>  
#include <stdlib.h>  
#include <pthread.h>  
  
#define THREAD_NUM 3  
#define REPEAT_NUM 3  
#define DELAY_TIME_LEVLES 5.0  
  
pthread_mutex_t mutex;  
  
void *thread_fun(void *arg)  
{  
    int no = (int) arg;  
    int delay;  
    int ret,i;  
  
    ret = pthread_mutex_lock(&mutex);  //返回0为成功  
    if(ret)  
    {  
        printf("Thread %d lock failed\n",no);  
        pthread_exit(NULL);  
    }  
  
    for(i=0;i<REPEAT_NUM;i++)  
    {  
        delay = (int)(rand()*DELAY_TIME_LEVELS/(RAND_MAX))+1;  
        sleep(delay);  
        printf("Thread %d: job %d delay = %d\n",no,i,delay);  
    }  
    printf("Thread %d finished\n",no);  
    pthread_exit  
}  
  
int main()  
{  
    pthread_t thread[THREAD_NUM];  
    int i,ret;  
    void *thrd;  
  
    srand(time(NULL));  
  
    pthread_mutex_init(&mutex,NULL);  
  
    for(i=0;i<THREAD_NUM;i++)  
    {  
        ret = pthread_create(&thread[i],NULL,thread_fun,(void *)i);  
        if(ret!=0)  
        {  
            printf("Create %d failed\n",i);  
            exit(ret);  
        }  
    }  
  
    printf("Create thread success!\n Waiting for threads to finish ...\n");  
  
    for(i=0;i<THREAD_NUM;i++)  
    {  
        ret = pthread_join(thread[i],&thrd);  
        pthread_mutex_unlock(&mutex);  
    }  
    pthread_mutex_destroy(&mutex);  
    return 0;  
}  


死锁现象:

死锁 (deallocks): 是指两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。


接下来介绍两个典型死锁现象例子,写程序时应避免此类现象。

1.线程发起pthread_mutex_lock()后,未解锁再次调用pthread_mutex_lock()进行上锁。此时线程会陷入无限等待状态。

2.线程A                                                               线程B

1.pthread_mutex_lock(&mutex1);                   1.pthread_mutex_lock(&mutex2);

2.pthread_mutex_lock(&mutex2);                   2.pthread_mutex_lock(&mutex1);

这两个线程中的每个线程都成功地锁住一个互斥量,接着试图对己为另一个线程锁定的互斥量加锁。两个线程将无限期地等待下去。


要避免此类死锁问题,最简单的方法是定义互斥量的层次关系。当多个线程对一组互斥量操作时,总是应该以相同的顺序对该组互斥量进行锁定。









  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值