线程同步方式

线程同步方式

一、线程同步

​ 在多线程编程里面,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。

​ 同一进程内的多个线程之间,除了栈内存是独立的,其他资源全部共享。

二、互斥锁
注意:如果man手册中查不到这系列函数,可以安装以下内容:
    sudo apt-get install glibc-doc
    sudo apt-get install manpages-posix-dev
    
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
功能:定义并初始化互斥锁
    
int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
功能:初始化一互斥锁,会被初始化为非锁定状态

int pthread_mutex_lock (pthread_mutex_t* mutex);
功能:加锁,当互斥锁已经是锁定状态时,调用者会阻塞,直到互斥被解开,当前线程才会加锁成功并返回。

int pthread_mutex_unlock (pthread_mutex_t* mutex);
功能:解锁,解锁后等待加锁的线程才能加锁成功。

int pthread_mutex_destroy (pthread_mutex_t* mutex);
功能:销毁锁
    
int pthread_mutex_trylock (pthread_mutex_t *__mutex)
功能:加测试锁,如果不加锁刚立即返回

int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
                            const struct timespec *restrict abs_timeout);
功能:倒计时加锁,如果超时还不加上则立即返回。
struct timespec{
	time_t tv_sec;        /* Seconds.  */
	long int tv_nsec;     /* Nanoseconds.*/
};
三、读写锁
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
功能:定义并初始化读写锁

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
                        const pthread_rwlockattr_t *restrict attr);
功能:初始化读写锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
功能:加读锁,如果不能加则阻塞等待
    
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
功能:加写锁,如果不能加则阻塞等待
    
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
功能:解锁写锁

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
功能:尝试加读锁,如果不能加则立即返回

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
功能:尝试加写锁,如果不能加则立即返回

int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock,
           const struct timespec *restrict abstime);
功能:带倒计时加读锁,超时则立即返回
    
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock,
           const struct timespec *restrict abstime);
功能:带倒计时加写锁,超时则立即返回

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
功能:销毁读写锁
    
//与互斥锁的区别是,它能让只读的线程加上锁
四、原子操作

​ 所谓的原子操作就是不可被拆分的操作,对于多线程对全局变量进行操作时,就再也不用再线程锁了

type __sync_fetch_and_add (type *ptr, type value);	//
type __sync_fetch_and_sub (type *ptr, type value);	// -
type __sync_fetch_and_and (type *ptr, type value);	// &
type __sync_fetch_and_or (type *ptr, type value);	// |
type __sync_fetch_and_nand (type *ptr, type value);	// ~
type __sync_fetch_and_xor (type *ptr, type value);	// ^
功能:以上操作返回的是*ptr的旧值

type __sync_add_and_fetch (type *ptr, type value); 	// +
type __sync_sub_and_fetch (type *ptr, type value);	// -
type __sync_and_and_fetch (type *ptr, type value);	// &
type __sync_or_and_fetch (type *ptr, type value);	// |
type __sync_nand_and_fetch (type *ptr, type value);	// ~
type __sync_xor_and_fetch (type *ptr, type value);	// ^
功能:以上操作返回的是*ptr与value计算后的值
    
type __sync_lock_test_and_set (type *ptr, type value);
功能:把value赋值给*ptr,并返回*ptr的旧值
    
__sync_lock_release(type *ptr);
功能:将*ptr赋值为0
#include <stdio.h>
#include <pthread.h>

int num = 0;
void* run(void* arg)
{
    for(int i=0; i<100000000; i++)
    {
		__sync_fetch_and_add(&num,1);
    }
}

int main(int argc,const char* argv[])
{
    pthread_t pid1,pid2;
    pthread_create(&pid1,NULL,run,NULL);
    pthread_create(&pid2,NULL,run,NULL);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    printf("%d\n",num);
}
原子操作的优点

速度快、不会产生死锁

原子操作的缺点:

该功能并不通用,有些编译器不支持;type只能是整数相关的类型,浮点型和自定义类型无法使用。

五、条件变量

​ 条件变量是利用线程间共享的"全局变量"进行同步的一种机制,主要包括两个动作:

​ 1、线程等待"条件变量的条件成立"而休眠;

​ 2、等"条件成立"叫醒休眠的线程。

​ 为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起,一般线程睡入条件变量,伴随着解锁动作,而线程从条件变量醒来时,伴随着加锁功能,如果加锁失败线程会进入阻塞状态而不是睡眠。

//初始化条件变量
int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr);
//亦可pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

// 使调用线程睡入条件变量cond,同时释放互斥锁mutex
int pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex);

int pthread_cond_timedwait (pthread_cond_t* cond,
    pthread_mutex_t* mutex,
    const struct timespec* abstime);

struct timespec {
    time_t tv_sec;  // Seconds
    long   tv_nsec; // Nanoseconds [0 - 999999999]
};

// 从条件变量cond中唤醒一个线程,令其重新获得原先的互斥锁
int pthread_cond_signal (pthread_cond_t* cond);

注意:被唤出的线程此刻将从pthread_cond_wait函数中返回,
但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。

// 从条件变量cond中唤醒所有线程
int pthread_cond_broadcast (pthread_cond_t* cond);

// 销毁条件变量
int pthread_cond_destroy (pthread_cond_t* cond);
六、信号量
1、多线程使用的信号量:
#include <semaphore.h>
sem_t sem;

int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:给信号量设置初始值
pshared:信号量的使用范围
    0 线程间使用 
    nonzero 进程之间使用//linux不支持

int sem_wait(sem_t *sem);
功能:信号量减1操作,如果信号量已经等于0,则阻塞

int sem_trywait(sem_t *sem);
功能:尝试对信号量减1操作,能减返回0成功,不能减返回-1失败,不会阻塞

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
功能:带倒计时的对信号减1操作,能减返回0成功,不能减超时返回-1失败,阻塞abs_timeout一段时间

int sem_post(sem_t *sem);
功能:对信号量执行加1操作

int sem_getvalue(sem_t *sem, int *sval);
功能:获取信号量的值

int sem_destroy(sem_t *sem);
功能:销毁信号量 
2、多进程使用的信号量:
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
功能:在内核创建一个信号量对象
name:信号量的名字
oflag:
    O_CREAT	不存在则创建信号量,存在则获取
    O_EXCL	如果信号量已经存在,返回失败
mode:信号量的权限
value:信号量的初始值
    
sem_t *sem_open(const char *name, int oflag);
功能:获取信号,或相关属性
    
int sem_unlink(const char *name);
功能:删除信号量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值