Linux下线程安全和锁

目录

什么是线程安全

概念

Linux下线程安全的实现

常见线程安全的实现方法

Linux线程互斥

互斥量

通过加锁实现线程安全

线程安全示例代码  

线程不安全的情况

常见的线程安全的情况


什么是线程安全

概念

线程安全是指在多线程环境下,对共享资源的访问会导致数据不一致或者出现其他异常情况。线程安全通常是通过同步机制来实现的,常见的同步机制包括互斥锁、条件变量、信号量等。

Linux下线程安全的实现

常见线程安全的实现方法

  • 代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。
  • 如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临界区。
  • 如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。

Linux线程互斥

背景概念

  • 临界资源:多线程执行流共享的资源就叫做临界资源
  • 临界区:每个线程内部,访问临界资源的代码,就叫做临界区
  • 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用
  • 原子性(后面讨论如何实现):不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成

互斥量

  •  大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个线程,其他线程无法获得这种变量。
  • 但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之间的交互。
  • 多个线程并发的操作共享变量,会带来一些问题。

要做到这三点,本质上就是需要一把锁。Linux上提供的这把锁叫互斥量。 

互斥量创建和初始化

#include
//创建互斥量
pthread_mutex_t mutex;
//互斥量静态分配
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
//动态分配
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict
attr);

//互斥量销毁
//使用PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁
//不要销毁一个已经加锁的互斥量
//已经销毁的互斥量,要确保后面不会有线程再尝试加锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);


//加锁,如果当前互斥量已经被其他线程锁定,则调用线程会被阻塞,直到互斥量被解锁。一旦成功获取锁,调用线程就可以访问被保护的资源。
int pthread_mutex_lock(pthread_mutex_t *mutex);

//该函数用于尝试非阻塞地锁定一个互斥量。如果当前互斥量已经被其他线程锁定,则调用线程不会被阻塞,而是立即返回一个失败的值,通常为 EBUSY 错误码(表示资源忙)。
int pthread_mutex_trylock(pthread_mutex_t *mutex);

返回值
如果创建成功则返回0,失败则返回错误码

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

通过加锁实现线程安全

Linux下线程安全和锁_第1张图片

线程安全示例代码  

#include 
#include 
#include 
#include 
#include 
#include 
int ticket = 100;
pthread_mutex_t mutex;
void *route(void *arg)
{
    char *id = (char *)arg;
    while (1)
    {
        //加锁
        pthread_mutex_lock(&mutex);
        if (ticket > 0)
        {
            usleep(1000);
            printf("%s sells ticket:%d\n", id, ticket);
            ticket--;
            //解锁
            pthread_mutex_unlock(&mutex);
            // sched_yield(); 放弃CPU
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
    }
}
int main(void)
{
    pthread_t t1, t2, t3, t4;
    pthread_mutex_init(&mutex, NULL);
    pthread_create(&t1, NULL, route, "thread 1");
    pthread_create(&t2, NULL, route, "thread 2");
    pthread_create(&t3, NULL, route, "thread 3");
    pthread_create(&t4, NULL, route, "thread 4");
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_join(t3, NULL);
    pthread_join(t4, NULL);
    pthread_mutex_destroy(&mutex);
}

线程不安全的情况

  • 不保护共享变量的函数
  • 函数状态随着被调用,状态发生变化的函数
  • 返回指向静态变量指针的函数
  • 调用线程不安全函数的函数 

常见的线程安全的情况

  • 每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的
  • 类或者接口对于线程来说都是原子操作
  • 多个线程之间的切换不会导致该接口的执行结果存在二义性

你可能感兴趣的:(开发语言,linux,c语言,c++)