Linux多线程——互斥量

目录

临界区

什么是互斥量

创建与销毁

1.创建互斥量

2.销毁互斥量

加锁与解锁

1.加锁

2.解锁

3.示例代码

死锁和避免

1.什么是死锁

2.死锁的避免


 


临界区

在计算机系统中有许多共享资源不允许用户并行使用。例如打印机,如果它同时进行两份文档打印,它的输出就会产生交错,从而都无法获得正确的文档。像打印机这样的共享设备被称为“排它性资源”,因为它一次只能由一个执行流访问。执行流必须以互斥的方式执行访问排它性资源的代码

临界区是必须以互斥方式执行的代码段,也就是说在临界区的范围内只能有一个活动的执行线程

什么是互斥量

互斥量,又称为互斥锁,是一种用来保护临界区的特殊变量,它可以处于锁定状态,也可以处于解锁状态:

(1)如果互斥锁是锁定的,就是一个特定的线程持有这个互斥锁

(2)如果没有线程持有这个互斥锁,那么这个互斥锁就处于解锁状态

每个互斥锁内部都有一个线程等待队列,用来保存等待该互斥锁的线程。当互斥锁处于解锁状态时,一个线程试图获取这个互斥锁时,这个线程就可以得到这个互斥锁而不会阻塞;当互斥锁处于锁定状态时,一个线程试图获取这个互斥锁时,这个线程将素色在互斥锁的等待线程队列内

互斥量是最简单也是最有效的县城同步机制。另外,互斥量只能被短时间的持有,使用完临界资源后应立即释放锁

创建与销毁

1.创建互斥量

pthreads使用pthread_mutex_t类型的变量来表示互斥量,同时在使用互斥量进行同步时需要先对它进行初始化,可以静态或动态方式对互斥量进行初始化

(1)静态初始化

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

(2)动态初始化

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

参数mutex是一个指向要初始化的互斥量的指针;参数attr传递NULL来初始化一个带有默认属性的互斥量,否则就要用类似于线程属性对象所使用的方法,先创建互斥量属性对象,再用该属性对象来创建互斥量

函数成功返回0,否则返回一个非0的错误码,下表为错误码:

错误码出错描述
EAGAIN系统缺乏初始化互斥量所需的非内存资源
ENOMEM系统缺乏初始化互斥量所需的内存资源
EPERM调用程序没有适当的优先级

以下代码用来动态的初始化默认属性的互斥量mylock:

int error;
pthread_mutex_t mylock;
if(error = pthread_mutex_init(&mylock,NULL))
    fprintf(stderr,"Failed to initialize mylock: %s\n",strerror(error));

2.销毁互斥量

销毁互斥量使用pthread_mutex_destroy()函数,原型如下:

int pthread_mutex_destroy(pthread_mutex_t *mutex);

参数mutex指向要销毁的互斥量的指针。以下代码销毁了mylock互斥量:

int error;
pthread_mutex_t mutex;
if(error = pthread_mutex_destroy(&mylock))
    fprintf(stderr,"Failed to destroy mylock:%s\n",stderror(error));

加锁与解锁

1.加锁

Pthreads中有两个试图锁定互斥量的函数,pthread_mutex_lock()和pthread_mutex_trylock(),pthread_mutex_lock()函数会一直阻塞到互斥量可用为止,而pthread_mutex_trylock()会尝试加锁,通常立即返回,函数原型如下:

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

参数mutex是需要加锁的互斥量。函数成功返回0,否则返回一个非0的错误码,其中另一个线程已持有锁的情况下,调用pthread_mutex_trylock()函数的额错误码是EBUSY

2.解锁

pthread_mutex_unlock()函数是用来释放指定的互斥量。函数原型如下:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

参数是需要解锁的互斥量。成功返回0;否则返回一个非0的错误码

只有当线程需要进入临界区之前正确地获取适当的互斥量,并在线程离开临界区时释放互斥量。以下伪代码展示了互斥量保护临界区的基本用法:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);

3.示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

pthread_t tid[2];
pthread_mutex_t lock;

void *doPrint(void *arg)
{
    int id=(long)arg;
    int i=0;
    pthread_mutex_lock(&lock);    /*使用互斥量保护临界区*/
    printf("Job %d started\n",id);
    for(int i=0;i<5;i++)
    {
        printf("Job %d printfing\n",id);
        usleep(10);
    }
    printf("Job %d finished\n",id);
    pthread_mutex_unlock(&lock);
    return NULL;
}

int main(int argc, char *argv[])
{
    long i=0;
    int err;

    if(pthread_mutex_init(&lock,NULL)!=0)
    {
        printf("Mutex init failed\n");
        return 1;
    }

    while(i<2)
    {
        err=pthread_create(&tid[i],NULL,&doPrint,(void *)i);
        if(err!=0)
            printf("Can't create thread:[%s]\n",strerror(err));
        i++;
    }
    pthread_join(tid[0],NULL);
    pthread_join(tid[1],NULL);
    pthread_mutex_destroy(&lock);

    return 0;
}

运行结果如下所示,Job1先获取互斥锁并进行打印,所有Job1需要打印的完成后才让Job0打印

死锁和避免

1.什么是死锁

死锁是指两个或两个以上的执行序在执行过程中,因争夺资源而造成的一种互相等待的现象。例如:一个线程T1已锁定一个资源R1,又想去锁定资源R2,而此时另一个线程T2已锁定了资源R2,却想去锁定R1,两个线程都想得到对方的资源,而不愿释放自己的资源,造成两个线程都在等待,而无法执行的情况。下程序清单示例了死锁发生的情况:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

pthread_t tid[2];
pthread_mutex_t mutexA=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutexB=PTHREAD_MUTEX_INITIALIZER;

void *t1(void *arg)
{
    pthread_mutex_lock(&mutexA);
    printf("t1 get mutexA\n");
    usleep(1000);
    pthread_mutex_lock(&mutexB);
    printf("t1 get mutexB\n");
    pthread_mutex_unlock(&mutexB);
    printf("t1 release mutexB\n");
    pthread_mutex_unlock(&mutexA);
    printf("t1 release mutexA\n");
    return NULL;
}

void *t2(void *arg)
{
    pthread_mutex_lock(&mutexB);
    printf("t2 get mutexA\n");
    usleep(1000);
    pthread_mutex_lock(&mutexA);
    printf("t2 get mutexB\n");
    pthread_mutex_unlock(&mutexA);
    printf("t2 release mutexB\n");
    pthread_mutex_unlock(&mutexB);
    printf("t2 release mutexA\n");
    return NULL;
}

int main(int argc, char *argv[])
{
    int err;

    err=pthread_create(&(tid[0]),NULL,&t1,NULL);
    if(err!=0)
        printf("Can't create thread:[%s]\n",strerror(err));

    err=pthread_create(&(tid[1]),NULL,&t2,NULL);
    if(err!=0)
        printf("Can't create thread:[%s]\n",strerror(err));

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

    return 0;
}

下图为代码运行结果,两个线程互相等待,进入死锁

2.死锁的避免

当多个线程需要相同的一些锁, 但是按照不同的顺序加锁, 死锁就很容易发生, 如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。 例如,规定程序内有三个互斥锁的加锁顺序为 mutexA->mutexB->mutexC,则线程 t1、 t2、 t3 线程操作伪代码如下所示:
 

t1t2t3
lock(mutexA)lock(mutexA)lock(mutexB)
lock(mutexB)lock(mutexC)lock(mutexC)
lock(mutexC)  

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值