Linux——线程安全、互斥锁的使用

作者:小 琛
欢迎转载,请标明出处

文章目录

    • 为什么会有线程安全问题
    • 互斥锁的引入
    • 互斥锁的使用函数接口
    • 例子:抢票系统
    • 死锁的概念

为什么会有线程安全问题

我们知道,多线程编程时,创建出来的线程对应同一个虚拟地址空间,即拥有相同的数据,假设多个线程同时运行,都在访问同一个临界资源,就会出现问题。
例如:
Linux——线程安全、互斥锁的使用_第1张图片

因此,为了避免程序出现二义性,要保证在同一时刻,一个临界资源只能被一个执行流访问。

互斥锁的引入

互斥锁:本质上是一个计数器,而这个计数器的取值只有0或者1。
0代表不能加锁,意味着不能去访问临界资源,互斥锁当中的加锁接口就会进行阻塞;
1代表可以枷锁,意味着可以去访问临界资源,互斥锁中的加锁接口会正常返回。

Linux——线程安全、互斥锁的使用_第2张图片

互斥锁的使用函数接口

定义互斥锁
pthread_mutex_t ://互斥锁变量类型

初始化互斥锁
int pthread_mutex_init (pthread_mutex_t* mutex, pthread_mutexattr_t * attr);
mutex: 传入互斥锁变量的地址
attr:设置互斥锁属性,通常我们使用默认属性传入NULL

加锁

  1. 阻塞加锁:
    int pthread_mutex_lock(pthread_mutex_t* lock)
  2. 非阻塞加锁:
    int pthread_mutex_trylock(pthread_mutex_t* lock)
    规则:如果互斥锁当中计数器的值为1,则表示可以加锁,正常调用;如果互斥锁当中的计数器值为0,表示不可以加锁,报错返回。
  3. 带有超时时间的加锁
    int pthread_mutext_timelock(pthread_mutex_t* lock,const struct timespec*)
    规则:当加锁时,若当前资源被其它执行流占用,则等待规定时间,若仍无法获取锁资源,报错返回

解锁
int pthread_mutex_unlock(pthread_mutex_t* lock)
执行流,不论采用哪种方式加锁,都可以调用该接口进行解锁。

摧毁互斥锁
int pthread_mutex_destory(pthread_mutex_t* lock)
释放动态初始化的互斥锁变量,防止内存泄漏

例子:抢票系统

假设有100张票,使用多线程编程,多个线程同时对100张票进行消耗,即- -操作,若不使用互斥锁,很可能导致某张票被抢好几次,很明显不符合逻辑。
Linux——线程安全、互斥锁的使用_第3张图片
使用如下代码,加入了互斥锁,一切正常

#include 
#include 
#include 

#define THREADCOUNT 4

int g_tickes = 100;
pthread_mutex_t lock;

void* ThreadStart(void* arg)
{
    (void)arg;
    while(1)
    {
        pthread_mutex_lock(&lock);
        if(g_tickes > 0)
        {
            g_tickes--; 
            usleep(100000);
            printf("i am thread [%p], i have ticket num is [%d]\n", pthread_self(), g_tickes + 1);
        }
        else
        {
            pthread_mutex_unlock(&lock);
            break;
        }
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}

int main()
{
    pthread_mutex_init(&lock, NULL);
    pthread_t tid[THREADCOUNT];
    int i = 0;
    for(; i < THREADCOUNT; i++)
    {
        int ret = pthread_create(&tid[i], NULL, ThreadStart, NULL);
        if(ret < 0)
        {
            perror("pthread_create");
            return 0;
        }
    }

    for(i = 0; i < THREADCOUNT; i++)
    {
        pthread_join(tid[i], NULL);
    }

    pthread_mutex_destroy(&lock);
    return 0;
}

死锁的概念

死锁:程序中有一个执行流没有释放资源,导致其它想获取该互斥锁的执行流进入阻塞等待,而该执行流还想申请对方的锁,形成闭环,导致死锁。
Linux——线程安全、互斥锁的使用_第4张图片

死锁的4个条件
1、互斥:每一把锁都只能被一个执行力占用
2、请求+保持:每一个执行流占用一个锁,但仍在申请新的锁
3、循环等待:若干个执行力在拥有和占用情况下,形成了一个闭环
4、不可剥夺:互斥锁只能被当前拥有者释放

避免死锁
1、破坏必要条件
2、加锁顺序一直
3、避免锁的不被释放
4、一次性分配资源

你可能感兴趣的:(Linux)