分享更有价值
被信任是一种快乐

linux多线程同步的方式有哪些

文章页正文上

这篇文章主要介绍了linux多线程同步的方式有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇linux多线程同步的方式有哪些文章都会有所收获,下面我们一起来看看吧。 同步方式有:1、互斥锁,是一个特殊全局变量,拥有lock和unlock两种状态,unlock互斥锁可由某个线程获得,当互斥锁由某个线程持有后,这个互斥锁会锁上变成lock状态,此后只有该线程有权力打开该锁;2、自旋锁,就是一个死循环,不停的轮询;3、信号量,是一个计数器,用于控制访问有限共享资源的线程数;4、条件变量;5、读写锁;6、屏障,是用户协调多个线程并行工作的同步机制。线程同步指的是当一个线程在对某个临界资源进行操作时,其他线程都不可以对这个资源进行操作,直到该线程完成操作,其他线程才能操作,也就是协同步调,让线程按预定的先后次序进行运行。线程同步的方法有6种:互斥锁、自旋锁、信号量、条件变量、读写锁、屏障。下面是一个线程不安全的例子:

#include
#include

intticket_num=10000000;

void*sell_ticket(void*arg){
while(ticket_num>0){
	ticket_num--;
}
}

intmain(){
pthread_tt1,t2,t3;
pthread_create(&t1,NULL,&sell_ticket,NULL);
pthread_create(&t2,NULL,&sell_ticket,NULL);
pthread_create(&t3,NULL,&sell_ticket,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t3,NULL);
printf("ticket_num=%dn",ticket_num);
return0;
}

运行结果如下:

#gccno_lock_demo.c-ono_lock_demo.out-pthread
#./no_lock_demo.out
ticket_num=-2

最后运行的结果不是固定的,有可能是0、-1,如果有这个ticket_num变量代表是库存的话,那么就会出现库存为负数的情况,所以需要引入线程同步来保证线程安全。Linux下提供了多种方式来处理线程同步,最常用的是互斥锁、自旋锁、信号量。互斥锁本质就是一个特殊的全局变量,拥有lock和unlock两种状态,unlock的互斥锁可以由某个线程获得,当互斥锁由某个线程持有后,这个互斥锁会锁上变成lock状态,此后只有该线程有权力打开该锁,其他想要获得该互斥锁的线程都会阻塞,直到互斥锁被解锁。互斥锁的类型:普通锁(PTHREAD_MUTEX_NORMAL):互斥锁默认类型。当一个线程对一个普通锁加锁以后,其余请求该锁的线程将形成一个 等待队列,并在该锁解锁后按照优先级获得它,这种锁类型保证了资源分配的公平性。一个 线程如果对一个已经加锁的普通锁再次加锁,将引发死锁;对一个已经被其他线程加锁的普 通锁解锁,或者对一个已经解锁的普通锁再次解锁,将导致不可预期的后果。检错锁(PTHREAD_MUTEX_ERRORCHECK):一个线程如果对一个已经加锁的检错锁再次加锁,则加锁操作返回EDEADLK;对一个已 经被其他线程加锁的检错锁解锁或者对一个已经解锁的检错锁再次解锁,则解锁操作返回 EPERM。嵌套锁(PTHREAD_MUTEX_RECURSIVE):该锁允许一个线程在释放锁之前多次对它加锁而不发生死锁;其他线程要获得这个锁,则当前锁的拥有者必须执行多次解锁操作;对一个已经被其他线程加锁的嵌套锁解锁,或者对一个已经解锁的嵌套锁再次解锁,则解锁操作返回EPERM。默认锁(PTHREAD_MUTEX_ DEFAULT):一个线程如果对一个已经加锁的默认锁再次加锁,或者虽一个已经被其他线程加锁的默 认锁解锁,或者对一个解锁的默认锁解锁,将导致不可预期的后果;这种锁实现的时候可能 被映射成上述三种锁之一。相关方法:

//静态方式创建互斥锁
pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER

//动态方式创建互斥锁,其中参数mutexattr用于指定互斥锁的类型,具体类型见上面四种,如果为NULL,就是普通锁。
intpthread_mutex_init(pthread_mutex_t*mutex,constpthread_mutexattr_t*mutexattr);

intpthread_mutex_lock(pthread_mutex_t*mutex);//加锁,阻塞
intpthread_mutex_trylock(pthread_mutex_t*mutex);//尝试加锁,非阻塞
intpthread_mutex_unlock(pthread_mutex_t*mutex);//解锁

例子:

#include
#include

intticket_num=10000000;

pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;

void*sell_ticket(void*arg){
while(ticket_num>0){
	pthread_mutex_lock(&mutex);
	if(ticket_num>0){
	ticket_num--;
	}
	pthread_mutex_unlock(&mutex);
}
}

intmain(){
pthread_tt1,t2,t3;
pthread_create(&t1,NULL,&sell_ticket,NULL);
pthread_create(&t2,NULL,&sell_ticket,NULL);
pthread_create(&t3,NULL,&sell_ticket,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t3,NULL);
printf("ticket_num=%dn",ticket_num);
return0;
}

自旋锁顾名思义就是一个死循环,不停的轮询,当一个线程未获得自旋锁时,不会像互斥锁一样进入阻塞休眠状态,而是不停的轮询获取锁,如果自旋锁能够很快被释放,那么性能就会很高,如果自旋锁长时间不能够被释放,甚至里面还有大量的IO阻塞,就会导致其他获取锁的线程一直空轮询,导致CPU使用率达到100%,特别CPU时间。相关方法:

intpthread_spin_init(pthread_spinlock_t*lock,intpshared)//创建自旋锁

intpthread_spin_lock(pthread_spinlock_t*lock)//加锁,阻塞
intpthread_spin_trylock(pthread_spinlock_t*lock)//尝试加锁,非阻塞
intpthread_spin_unlock(pthread_spinlock_t*lock)//解锁

例子:

#include
#include

intticket_num=10000000;

pthread_spinlock_tspinlock;

void*sell_ticket(void*arg){
while(ticket_num>0){
	pthread_spin_lock(&spinlock);
	if(ticket_num>0){
	ticket_num--;
	}
	pthread_spin_unlock(&spinlock);
}
}

intmain(){
pthread_spin_init(&spinlock,0);
pthread_tt1,t2,t3;
pthread_create(&t1,NULL,&sell_ticket,NULL);
pthread_create(&t2,NULL,&sell_ticket,NULL);
pthread_create(&t3,NULL,&sell_ticket,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t3,NULL);
printf("ticket_num=%dn",ticket_num);
return0;
}

信号量是一个计数器,用于控制访问有限共享资源的线程数。相关方法:

//创建信号量
//pshared:一般取0,表示调用进程的信号量。非0表示该信号量可以共享内存的方式,为多个进程所共享(Linux暂不支持)。
//value:信号量的初始值,可以并发访问的线程数。
intsem_init(sem_t*sem,intpshared,unsignedintvalue);

intsem_wait(sem_t*sem);//信号量减1,信号量为0时就会阻塞

intsem_trywait(sem_t*sem);//信号量减1,信号量为0时返回-1,不阻塞

intsem_timedwait(sem_t*sem,conststructtimespec*abs_timeout);//信号量减1,信号量为0时阻塞,直到abs_timeout超时返回-1

intsem_post(sem_t*sem);//信号量加1

例子:

#include
#include
#include

intticket_num=10000000;

sem_tsem;

void*sell_ticket(void*arg){
while(ticket_num>0){
	sem_wait(&sem);
	if(ticket_num>0){
	ticket_num--;
	}
	sem_post(&sem);
}
}

intmain(){
sem_init(&sem,0,1);//value=1表示最多1个线程同时访问共享资源,与互斥量等价
pthread_tt1,t2,t3;
pthread_create(&t1,NULL,&sell_ticket,NULL);
pthread_create(&t2,NULL,&sell_ticket,NULL);
pthread_create(&t3,NULL,&sell_ticket,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t3,NULL);
printf("ticket_num=%dn",ticket_num);
return0;
}

条件变量可以让调用线程在满足特定条件的情况下运行,不满足条件时阻塞等待被唤醒,必须与互斥锁搭配使用。条件变量常用于生产者与消费者模型。相关方法:

pthread_cond_tcond=PTHREAD_COND_INITIALIZER;//创建条件变量,一个互斥锁可以对应多个条件变量

intpthread_cond_wait(pthread_cond_t*cond,pthread_mutex_t*mutex);//阻塞等待条件满足,同时释放互斥锁mutex

intpthread_cond_timedwait(pthread_cond_t*cond,
pthread_mutex_t*mutex,
conststructtimespec*abstime);//带超时的阻塞等待条件满足,同时释放互斥锁mutex

//从条件变量cond中唤出一个线程,令其重新获得原先的互斥锁
//被唤出的线程此刻将从pthread_cond_wait函数中返回,但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。
intpthread_cond_signal(pthread_cond_t*cond);

//从条件变量cond中唤出所有线程
intpthread_cond_broadcast(pthread_cond_t*cond);

例子:

#include
#include

intmax_buffer=10;
intcount=0;

pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_tnotempty=PTHREAD_COND_INITIALIZER;
pthread_cond_tnotfull=PTHREAD_COND_INITIALIZER;

void*produce(void*args){
while(1){
pthread_mutex_lock(&mutex);
while(count==max_buffer){
printf("bufferisfull,wait...n");
pthread_cond_wait(&notfull,&mutex);
}
printf("produce...n");
count++;
sleep(1);
pthread_cond_signal(&notempty);
pthread_mutex_unlock(&mutex);
}

}

void*consumer(void*args){
while(1){
pthread_mutex_lock(&mutex);
while(count==0){
printf("bufferisempty,wait...n");
pthread_cond_wait(&notempty,&mutex);
}
printf("consumer...n");
count--;
sleep(1);
pthread_cond_signal(&notfull);
pthread_mutex_unlock(&mutex);
}

}

intmain(){
pthread_tt1,t2,t3,t4;
pthread_create(&t1,NULL,&produce,NULL);
pthread_create(&t2,NULL,&produce,NULL);

pthread_create(&t3,NULL,&consumer,NULL);
pthread_create(&t4,NULL,&consumer,NULL);

pthread_join(t1,NULL);
return0;
}

读写锁可以有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。读写锁也叫做共享-独占锁,当读写锁以读模式锁住时,它是以共享模式锁住的,当它以写模式锁住时,它是以独占模式锁住的,读读共享,读写互斥。相关方法:

//创建读写锁
pthread_rwlock_trwlock=PTHREAD_RWLOCK_INITIALIZER;

intpthread_rwlock_rdlock(pthread_rwlock_t*rwlock)//加读锁,阻塞
intpthread_rwlock_wrlock(pthread_rwlock_t*rwlock)//加写锁,阻塞
intpthread_rwlock_unlock(pthread_rwlock_t*rwlock)//释放读锁或者写锁

intpthread_rwlock_tryrdlock(pthread_rwlock_t*rwlock)//尝试加读锁,非阻塞
intpthread_rwlock_trywrlock(pthread_rwlock_t*rwlock)//尝试加写锁,非阻塞

例子:

#include
#include

pthread_rwlock_trwlock=PTHREAD_RWLOCK_INITIALIZER;

void*read(void*arg){
while(1){
pthread_rwlock_rdlock(&rwlock);
rintf("readmessage.n");
sleep(1);
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
}
void*write(void*arg){
while(1){
pthread_rwlock_wrlock(&rwlock);
printf("writemessage.n");
sleep(1);
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
}

intmain(intargc,char*argv[]){
pthread_tt1,t2,t3;
pthread_create(&t1,NULL,&read,NULL);
pthread_cre免费云主机、域名ate(&t2,NULL,&read,NULL);

pthread_create(&t3,NULL,&write,NULL);

pthread_join(t1,NULL);
return0;
}

屏障(barrier)是用户协调多个线程并行工作的同步机制。屏障允许每个线程等待,直到所有的合作线程都到达某一点,然后所有线程都从该点继续执行。pthread_join函数就是一种屏障,允许一个线程等待,直到另一个线程退出。但屏障对象的概念更广,允许任意数量的线程等待,直到所有的线程完成处理工作,而线程不需要退出,当所有的线程达到屏障后可以接着工作。相关方法:

//创建屏障
intpthread_barrier_init(pthread_barrier_t*barrier,constpthread_barrrierattr_t*attr,unsignedintcount)

//阻塞等待,直到所有线程都到达
intpthread_barrier_wait(pthread_barrier_t*barrier)

例子:

#include
#include

pthread_barrier_tbarrier;

void*go(void*arg){
sleep(rand()%10);
printf("%luisarrived.n",pthread_self());
pthread_barrier_wait(&barrier);
printf("%lugoshopping...n",pthread_self());
}

intmain(){
pthread_barrier_init(&barrier,NULL,3);

pthread_tt1,t2,t3;
pthread_create(&t1,NULL,&go,NULL);
pthread_create(&t2,NULL,&go,NULL);
pthread_create(&t3,NULL,&go,NULL);

pthread_join(t1,NULL);
return0;
}

关于“linux多线程同步的方式有哪些”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“linux多线程同步的方式有哪些”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注云技术行业资讯频道。

相关推荐: windows 0xc0000f如何解决

本文小编为大家详细介绍“windows 0xc0000f如何解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“windows 0xc0000f如何解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。方法一: 1、首先我们重启电脑,按…

文章页内容下
赞(0) 打赏
版权声明:本站采用知识共享、学习交流,不允许用于商业用途;文章由发布者自行承担一切责任,与本站无关。
文章页正文下
文章页评论上

云服务器、web空间可免费试用

宝塔面板主机、支持php,mysql等,SSL部署;安全高速企业专供99.999%稳定,另有高防主机、不限制内容等类型,具体可咨询QQ:360163164,Tel同微信:18905205712

主机选购导航云服务器试用

登录

找回密码

注册