Linux互斥锁及其应用

文章目录

互斥锁

作用: 防止多线程对同一个数据同时进行操作

在线程实际运行过程中,我们经常需要多个线程保持同步。这时可以用互斥锁来
完成任务。

1.1锁的创建

互斥锁可以动态或静态的被创建
可以用宏PTHREAD_MUTEX_INITIALIZER来静态的初始化锁,采用这种方式比较容易理解

互斥锁是pthread_mutex_t的结构体,而这个宏是一个结构常量,如下可以完成静态的初始化锁:

pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;

动态创建是通过pthread_mutex_init函数实现,函数原型如下:

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

其中:

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

1.2 锁操作

对锁的操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()
和测试加锁pthread_mutex_trylock()三个,函数原型如下:

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

pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

1.3 锁销毁

创建的互斥锁在不使用的时候需要消耗,不然会造成系统资源的流失,其函数原
型如下:

int pthread_mutexattr_destroy(pthread_mutex_t *mutex);

1.4互斥锁属性

使用互斥锁(互斥)可以使线程按顺序执行。通常,互斥锁通过确保一次只有一个线程执行代码的临界段来同步多个线程。互斥锁还可以保护单线程代码。
要更改缺省的互斥锁属性,可以对属性对象进行声明和初始化。通常,互斥锁属性会设置在应用程序开头的某个位置,以便可以快速查找和轻松修改。

初始化互斥锁属性对象

使用pthread_mutexattr_init(3C)可以将与互斥锁对象相关联的属性初始化为其缺省值。在执行过程中,线程系统会为每个属性对象分配存储空间。

pthread_mutexattr_init 语法

int pthread_mutexattr_init(pthread_mutexattr_t *mattr); 

#include <pthread.h> 
pthread_mutexattr_t mattr; 
int ret;/* initialize an attribute to default value */ 
ret = pthread_mutexattr_init(&mattr);

调用此函数时,pthread 属性的缺省值为 PTHREAD_PROCESS_PRIVATE。 该值表示可以在进程内使用经过初始化的互斥锁。

mattr 的类型为 opaque,其中包含一个由系统分配的属性对象。mattr 范围可能的值为 PTHREAD_PROCESS_PRIVATE 和 PTHREAD_PROCESS_SHARED。 PTHREAD_PROCESS_PRIVATE 是缺省值。
对于互斥锁属性对象,必须首先通过调用 pthread_mutexattr_destroy(3C) 将其销毁,才能重新初始化该对象。pthread_mutexattr_init() 调用会导致分配类型为 opaque 的对象。如果未销毁该对象,则会导致内存泄漏。

pthread_mutexattr_init 返回值

pthread_mutexattr_init() 成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。

ENOMEM 描述:内存不足,无法初始化互斥锁属性对象。

销毁互斥锁属性对象

pthread_mutexattr_destroy(3C)可用来取消分配用于维护
pthread_mutexattr_init() 所创建的属性对象的存储空间。

pthread_mutexattr_destroy 语法


int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr) 

#include <pthread.h> 
pthread_mutexattr_t mattr; 
int ret;/* destroy an attribute */ ret = pthread_mutexattr_destroy(&mattr); 

pthread_mutexattr_destroy 返回值

pthread_mutexattr_destroy() 成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。

EINVAL 描述: 由 mattr 指定的值无效。

案例
/*************************************************************************
    > File Name: pthread.c
    > Author: 杨永利
    > Mail: 1795018360@qq.com 
    > Created Time: 2020年07月27日 星期一 20时42分45秒
 ************************************************************************/

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

void * thread_addi(void *arg)
{
    int* tmp=arg;
    int i=0;
    for(i;i<1000000;i++)
    {
        ++(*tmp);
        //printf("%d\n",i);
    }

    return NULL;
}

int main(int argc, char* argv[]){
    int i=0;
    printf("我的初始化i是:%d\n",i);
    long int s=(long int)&i;
    pthread_t pid;
    pthread_create(&pid,NULL,thread_addi,&i);
    int j=0;
    for(j;j<1000000;j++)
    {
        i++;
        //printf("%d\n",i);
    }
    void *v;
    pthread_join(pid,v);
    printf("%s\n",(char*)v);
    printf("i最终的值为:%d\n",i);
    
    return 0;
}

遇到问题

运行之后我们会发现i的最终结果里2000000差很多,这是为什么呢???

这是因为两个线程的时间片分配时产生了干扰,当数特别小时干扰是看不到的,但是当进行的操作增大时,系统在给两个线程分配时间片时就会产生干扰,也就是上个例子中和最终加不满的问题。

那么 我们就在加数操作之后来加一句输出延缓时间,看看能不能加到2000000,
Linux互斥锁及其应用
我们可以看到这已经很接近2000000了,但是还是无法加到最后

那么怎样改进呢?这就用到了我们的互斥锁。

解决方案

在加数操作上加上加锁和解锁操作。

1.在循环外加锁解锁

Linux互斥锁及其应用

结果Linux互斥锁及其应用

2.在循环内加锁解锁(效率最高)

/*************************************************************************
    > File Name: pthread.c
    > Author: 杨永利
    > Mail: 1795018360@qq.com 
    > Created Time: 2020年07月27日 星期一 20时42分45秒
 ************************************************************************/

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

pthread_mutex_t mutex;

void * thread_addi(void *arg)
{
    int* tmp=arg;
    int i=0;
    for(i;i<1000000;i++)
    {
        pthread_mutex_lock(&mutex);
        ++(*tmp);
        //printf("%d\n",i);
        pthread_mutex_unlock(&mutex);
    }

    return NULL;
}

int main(int argc, char* argv[]){
    int i=0;
    printf("我的初始化i是:%d\n",i);
    long int s=(long int)&i;
    int err=pthread_mutex_init(&mutex,NULL);
    if(err!=0)
    {
        printf("锁创建失败\n");
        return -1;
    }

    pthread_t pid;
    pthread_create(&pid,NULL,thread_addi,&i);
    int j=0;
    for(j;j<1000000;j++)
    {
        pthread_mutex_lock(&mutex);
        i++;
        //printf("%d\n",i);
        pthread_mutex_unlock(&mutex);
    }
    void *v;
    pthread_join(pid,v);
    printf("%s\n",(char*)v);
    printf("i最终的值为:%d\n",i);
    
    return 0;
}

上一篇:多线程同步


下一篇:VC++线程同步之临界区(CriticalSection)