锁、条件变量、信号量

线程同步

互斥锁

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>

//定义一把锁
pthread_mutex_t mutex;

void *mythread1(void* args)
{
    
    
    while(1)
    {
    
    
        //加锁
        pthread_mutex_lock(&mutex);

        printf("hello");
        sleep(rand()%3);
        printf(" world\n");

        //解锁
        pthread_mutex_unlock(&mutex);
        sleep(rand()%3);
    }
    pthread_exit(NULL);
}

void *mythread2(void* args)
{
    
    
    while(1)
    {
    
    
        //加锁
        pthread_mutex_lock(&mutex);

        printf("HELLO");
        sleep(rand()%3);
        printf(" WORLD\n");

        //解锁
        pthread_mutex_unlock(&mutex);
        sleep(rand()%3);
    }
    pthread_exit(NULL);
}

int main()
{
    
    
    int ret;
    pthread_t thread1;
    pthread_t thread2;

    //随机数种子
    srand(time(NULL));

    //互斥锁初始化
    pthread_mutex_init(&mutex,NULL);

    ret=pthread_create(&thread1,NULL,mythread1,NULL);
    if(ret!=0)
    {
    
    
        printf("pthread_create error,[%s]\n",strerror(ret));
        return -1;
    }

    ret=pthread_create(&thread2,NULL,mythread1,NULL);
    if(ret!=0)
    {
    
    
        printf("pthread_create error,[%s]\n",strerror(ret));
        return -1;
    }

    //等待线程结束
    pthread_join(thread1,NULL);
    pthread_join(thread2,NULL);

    //释放互斥锁
    pthread_mutex_destroy(&mutex);
    return 0;

}

必须在所有操作共享资源的线程上都加上锁否则不能起到同步的效果

死锁

死锁并不是linux提供给用户的一种使用方法,而是由于用户使用互斥锁不当引起的一种现象

第一种

自己锁自己

void *mythread1(void* args)
{
    
    
    while(1)
    {
    
    
        //加锁
        pthread_mutex_lock(&mutex);
        pthread_mutex_lock(&mutex);

        printf("hello");
        sleep(rand()%3);
        printf(" world\n");

        //解锁
        pthread_mutex_unlock(&mutex);
        sleep(rand()%3);
    }
    pthread_exit(NULL);
}

第二种

线程A拥有A锁,请求获得B锁;线程B拥有B锁,请求获得A锁,这样造成线程A和线程B都不释放自己的锁,而且还想得到对方的锁,从而产生死锁

image-20211125153226385

解决:

  • 让线程按照一定顺序访问共享资源
  • 在访问其他锁的时候,需要先将自己的锁解开
  • 调用pthread_mutex_trylock,如果加锁不成功会立刻返回

读写锁

读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的

写独占、读共享

适合于对数据结构读的次数远大于写的情况

  • 读写锁是“写模式加锁”时,解锁前,所有对该锁加锁的线程都会被阻塞
  • 读写锁是“读模式加锁”时,如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞
  • 读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高
//定义一把读写锁
pthread_rwlock_t rwlock;

//初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
//rwlock-读写锁
//attr-读写锁属性,传NULL为默认属性

//销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);        

//加读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);              

//尝试加读锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

//加写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

//尝试加写锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

//解锁
int pthread_rwlock_unlock(&pthread_rwlock_t *rwlock);

条件变量

条件本身不是锁,但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所

  • 使用互斥量保护共享数据
  • 使用条件变量可以使线程阻塞, 等待某个条件的发生, 当条件满足的时候解除阻塞

两个动作

  • 条件不满足,阻塞线程
  • 条件满足,通知阻塞的线程解除阻塞开始工作

相关函数

pthread_cond_t  cond;
//定义一个条件变量

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
//函数描述:初始化条件变量
//cond: 条件变量
//attr: 条件变量属性, 通常传NULL
//成功返回0, 失败返回错误号

int pthread_cond_destroy(pthread_cond_t *cond);
//销毁条件变量
//函数参数: 条件变量
//返回值: 成功返回0, 失败返回错误号

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
//条件不满足, 引起线程阻塞并解锁;条件满足, 解除线程阻塞, 并加锁
//cond: 条件变量
//mutex: 互斥锁变量
//函数返回值: 成功返回0, 失败返回错误号

int pthread_cond_signal(pthread_cond_t *cond);
//唤醒至少一个阻塞在该条件变量上的线程
//函数参数: 条件变量
//函数返回值: 成功返回0, 失败返回错误号

信号量

相当于多把锁,可以理解为加强版的互斥锁

相关函数

//定义信号量 
sem_t sem;


int sem_init(sem_t *sem, int pshared, unsigned int value);	
//初始化信号量
//sem: 信号量变量
//pshared: 0表示线程同步, 1表示进程同步
//value: 最多有几个线程操作共享数据
//成功返回0, 失败返回-1, 并设置errno值

int sem_wait(sem_t *sem);
//调用该函数一次, 相当于sem--, 当sem为0的时候, 引起阻塞
//函数参数: 信号量变量
//成功返回0, 失败返回-1, 并设置errno值

int sem_post(sem_t *sem);
//调用一次, 相当于sem++
//函数参数: 信号量变量
//成功返回0, 失败返回-1, 并设置errno值

int sem_trywait(sem_t *sem);
//尝试加锁, 若失败直接返回, 不阻塞
//函数参数: 信号量变量
//函数返回值: 成功返回0, 失败返回-1, 并设置errno值

int sem_destroy(sem_t *sem);
//销毁信号量
//函数参数: 信号量变量
//函数返回值: 成功返回0, 失败返回-1, 并设置errno值

猜你喜欢

转载自blog.csdn.net/blll0/article/details/121539433