浅析Linux之线程间的同步与互斥

目录

同步与互斥的概述

互斥

同步

互斥锁(互斥量)

类型

初始化

申请上锁

解锁

销毁锁

组合拳

利用标志位实现互斥

死锁

读写锁

解决

特点

规则

初始化

申请读锁

申请写锁

释放读写锁

销毁读写锁

信号量

功能

分类

本质

支持的操作

实现互斥

实现同步

无名信号量的使用

初始化

信号量减一

尝试对信号量减一

信号量加一

销毁信号量

有名信号量的使用

打开/创建

关闭

删除

同步与互斥的概述

互斥

是指散布在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。最基本的场景就是:一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。

同步

是指散布在不同任务之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。最基本的场景就是:两个或两个以上的进程或线程在运
行过程中协同步调,按预定的先后次序运行。

互斥锁(互斥量)

  • 在访问共享资源后临界区域前,对互斥锁进行加锁。
  • 在访问完成后释放互斥锁导上的锁。
  • 对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。

类型

pthread_mutex_t

初始化

#include <pthread.h>

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

功能:
    初始化一个互斥锁。
参数:
    mutex:互斥锁地址。类型是 pthread_mutex_t 。
    attr:设置互斥量的属性,通常可采用默认属性,即可将 attr 设为 NULL。可以使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁,比如:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_mutex_init() 来完成动态初始化,不同之处在于 PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。
返回值:
    成功:0,成功申请的锁默认是打开的。
    失败:非 0 错误码

restrict,C语言中的一种类型限定符(Type Qualifiers),用于告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的方式修改该对象的内容。
 

申请上锁

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);

功能:
    对互斥锁上锁,若互斥锁已经上锁,则调用者阻塞,直到互斥锁解锁后再上锁。
参数:
    mutex:互斥锁地址。
返回值:
    成功:0
    失败:非 0 错误码


int pthread_mutex_trylock(pthread_mutex_t *mutex);
    调用该函数时,若互斥锁未加锁,则上锁,返回 0;
    若互斥锁已加锁,则函数直接返回失败,即 EBUSY

解锁

#include <pthread.h>

int pthread_mutex_unlock(pthread_mutex_t *mutex);

功能:
    对指定的互斥锁解锁。
参数:
    mutex:互斥锁地址。
返回值:
    成功:0
    失败:非0错误码

销毁锁

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

功能:
    销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。
参数:
    mutex:互斥锁地址。
返回值:
    成功:0
    失败:非 0 错误码

组合拳

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex;
//将字符串按照字符一个一个输出(1s输出一次)
void myprint(char *str)
{
    while(*str != '\0'){
        putchar(*str++);
        fflush(stdout);
        sleep(1);
    }
    printf("\n");
}
void *my_pthread_fun1(void * arg){
    pthread_mutex_lock(&mutex);
    
    myprint("hello");
    pthread_mutex_unlock(&mutex);
}
void *my_pthread_fun(void * arg){    
    //pthread_mutex_lock(&mutex);
    int ret;
    while(1){
        ret=pthread_mutex_trylock(&mutex);
        if(ret==0)
            break;
    }
    myprint("world");
    pthread_mutex_unlock(&mutex);
    
}
int main()
{

    
    pthread_mutex_init(&mutex,NULL);
    pthread_t tid,tid1;
    //任务1
    pthread_create(&tid,NULL,my_pthread_fun,NULL);
    //任务2
    pthread_create(&tid1,NULL,my_pthread_fun1,NULL);
    
    pthread_join(tid,NULL);
    pthread_join(tid1,NULL);
     pthread_mutex_destroy(&mutex);
    
    
    return 0;
}

利用标志位实现互斥

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
int flag=0;  //锁开   flag=1锁关
//将字符串按照字符一个一个输出(1s输出一次)
void myprint(char *str)
{
	while(*str != '\0'){
		putchar(*str++);
		fflush(stdout);
		sleep(1);
	}
	printf("\n");
}
void *my_pthread_fun1(void * arg){
	while(1){
		if(flag==0)
        
			break;
	}
	flag++;
	myprint("hello");	
	flag=0;	
}
void *my_pthread_fun(void * arg){	
	while(1){
		if(flag==0)
			break;
	}
	flag++;
	myprint("world");	
	flag=0;	
}
int main()
{
	
	pthread_t tid,tid1;
	//任务1
	pthread_create(&tid,NULL,my_pthread_fun,NULL);
	//任务2
	pthread_create(&tid1,NULL,my_pthread_fun1,NULL);
	
	pthread_join(tid,NULL);
	pthread_join(tid1,NULL);
	
	return 0;
}

死锁

读写锁

解决

互斥且提高效率(读共享,写互斥)

特点

  • 如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作。
  • 如果有其它线程写数据,则其它线程都不允许读、写操作

规则

  • 如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁。
  • 如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁

初始化


#include <pthread.h>

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
                    const pthread_rwlockattr_t *restrict attr);
功能:
    用来初始化 rwlock 所指向的读写锁。
参数:
    rwlock:指向要初始化的读写锁指针。
    attr:读写锁的属性指针。如果 attr 为 NULL 则会使用默认的属性初始化读写锁,否则使用指定的attr 初始化读写锁。
    可以使用宏 PTHREAD_RWLOCK_INITIALIZER 静态初始化读写锁,比如:pthread_rwlock_t my_rwlock = PTHREAD_RWLOCK_INITIALIZER;这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_rwlock_init() 来完成动态初始化,不同之处在于PTHREAD_RWLOCK_INITIALIZER 宏不进行错误检查。
返回值:
    成功:0,读写锁的状态将成为已初始化和已解锁。
    失败:非 0 错误码。

申请读锁

#include <pthread.h>

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

功能:
    以阻塞方式在读写锁上获取读锁(读锁定)。
    如果没有写者持有该锁,并且没有写者阻塞在该锁上,则调用线程会获取读锁。
    如果调用线程未获取读锁,则它将阻塞直到它获取了该锁。一个线程可以在一个读写锁上多次执行读锁定。
    线程可以成功调用 pthread_rwlock_rdlock() 函数 n 次,但是之后该线程必须调用pthread_rwlock_unlock() 函数 n 次才能解除锁定。
参数:
    rwlock:读写锁指针。
返回值:
    成功:0
    失败:非 0 错误码


int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    用于尝试以非阻塞的方式来在读写锁上获取读锁。
    如果有任何的写者持有该锁或有写者阻塞在该读写锁上,则立即失败返回

申请写锁

#include <pthread.h>

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

功能:
    在读写锁上获取写锁(写锁定)。
    如果如果调用线程未获取写锁,则它将阻塞直到它获取了该锁。
参数:
    rwlock:读写锁指针。
返回值:
    成功:0
    失败:非 0 错误码


int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
    用于尝试以非阻塞的方式来在读写锁上获取写锁。
    如果有任何的读者或写者持有该锁,则立即失败返回。没有写者持有该锁,并且没有写者读者持有该锁,则调    用线程会获取写锁

释放读写锁

#include <pthread.h>

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

功能:
    无论是读锁或写锁,都可以通过此函数解锁。
参数:
    rwlock:读写锁指针。
返回值:
    成功:0
    失败:非 0 错误码

销毁读写锁

#include <pthread.h>

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

功能:
    用于销毁一个读写锁,并释放所有相关联的资源(所谓的所有指的是由 pthread_rwlock_init() 自动申请的资源) 。
参数:
    rwlock:读写锁指针。
返回值:
    成功:0
    失败:非 0 错误码

信号量

功能

实现互斥,实现同步

分类

无名信号量

有名信号量

本质

非负整数

支持的操作

pv操作,p给信号量-1,v给信号量+1

实现互斥

信号量如果<1,进行p操作会阻塞

初始化一个信号量,默认值=1,在要实现互斥的任务中添加p操作,执行线程任务,然后再执行v操作。

实现同步

实现多少个任务同步就初始化多少个信号量,初始值是第一个要执行的任务中信号量=1,其余全部=0,在所有任务中给自己的信号量p操作,然后执行线程任务,然后给下一个要执行任务中信号量进行v操作

无名信号量的使用

初始化

#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value)

功能:
    创建一个信号量并初始化它的值。一个无名信号量在被使用前必须先初始化。
参数:
    sem:信号量的地址
    pshared:等于 0,信号量在线程间共享(常用);不等于0,信号量在进程间共享。
    value:信号量的初始值
返回值:
    成功:0
    失败: - 1

信号量减一

int sem_wait(sem_t *sem);

功能: 将信号量减一,如果信号量的值为0 则阻塞,大于0可以减一
参数:信号量的地址
返回值:成功返回0 失败返回-1

尝试对信号量减一

int sem_trywait(sem_t *sem);

功能: 尝试将信号量减一,如果信号量的值为0 不阻塞,立即返回 ,大于0可以减一
参数:信号量的地址
返回值:成功返回0 失败返回-1

信号量加一

int sem_post(sem_t *sem);

功能:将信号量加一
参数:信号量的地址
返回值:成功返回0 失败返回-1

销毁信号量

int sem_destroy(sem_t *sem);

功能: 销毁信号量
参数: 信号量的地址
返回值:成功返回0 失败返回-1

有名信号量的使用

打开/创建

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

当信号量存在时使用:
    sem_t *sem_open(const char *name, int oflag);
当信号量不存在时使用:
    sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned intvalue);
功能:
    创建一个信号量。
参数:
    name:信号量文件名。
    flags: sem_open 函数的行为标志。
    mode:文件权限(可读、可写、可执行)的设置。
    value :信号量初始值。
返回值:
    成功返回信号量的地址,失败返回 SEM_FAILED

关闭

#include <semaphore.h>

int sem_close(sem_t *sem);

功能:
    关闭有名信号量。
参数:
    sem:指向信号量的指针。
返回值:成功返回 0,失败返回-1

删除

#include <semaphore.h>

int sem_unlink(const char *name);

功能:
    删除信号量的文件。
参数:
    name:信号量文件名。
返回值:
    成功返回 0,失败返回-1

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞翔的杨先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值