线程同步
线程同步,指的是一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其它线程为保证数据一致性,不能调用该功能。 多个控制流,共同操作一个共享资源的情况,都需要同步
数据混乱原因 1.资源共享(独享资源不会) 2.调度随机(意味着数据访问会出现竞争) 3.线程间缺乏必要同步机制 restrict关键字 用来限定指针变量,被该关键字限定的指针变量所指向的内存操作,必须由本指针完成
互斥锁
概述
每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。
但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。 但,应注意:同一时刻,只能有一个线程持有该锁。
当A线程对某个全局变量加锁访问,B在访问前尝试加锁,拿不到锁,B阻塞。C线程不去加锁,而直接访问该全局变量,依然能够访问,但会出现数据混乱。
所以,互斥锁实质上是操作系统提供的一把“建议锁”(又称“协同锁”),建议程序中有多线程访问共享资源的时候使用该机制。但,并没有强制限定。 因此,即使有了mutex,如果有线程不按规则来访问数据,依然会造成数据混乱。
所需要的函数
pthread_mutex_t lock;
pthread_mutex_init();
pthread_mutex_lock();
pthread_mutex_unlock();
pthread_mutex_destroy();
效果展示
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>
pthread_mutex_t mutex;
void*tfn(void*argv)
{
srand(time(NULL));
while(1)
{
pthread_mutex_lock(&mutex);
printf("hello ");
sleep(rand()%2);
printf("world\n");
pthread_mutex_unlock(&mutex);
sleep(rand()%2);
}
return NULL;
}
int main()
{
pthread_t tid;
srand(time(NULL));
int ret=pthread_mutex_init(&mutex,NULL);
if(ret!=0)
{
fprintf(stderr,"mutex_init_error:%s\n",strerror(ret));
exit(1);
}
ret=pthread_create(&tid,NULL,tfn,NULL);
if(ret!=0)
{
fprintf(stderr,"create error:%s\n",strerror(ret));
exit(1);
}
while(1)
{
pthread_mutex_lock(&mutex);
printf("HELLO ");
sleep(rand()%2);
printf("WORLD\n");
pthread_mutex_unlock(&mutex);
sleep(rand()%2);
}
pthread_join(tid,NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
此代码让子线程打印 hello 和 world,让主线程打印HELLO 和 WORLD 如果不加互斥锁的话,打印出来的大小写是不对应的如下图 当我们加入互斥锁效果如下
读写锁
概述
锁只有一把,以读方式给数据加锁--------读锁。以写方式给数据加锁---------写锁。 读共享,写独占。 写锁优先级高。
所需要的函数
pthread_rwlock_t rwlock;
pthread_rwlock_init();
pthread_rwlock_rdlock();
pthread_rwlock_wrlock();
pthread_rwlock_trywrlock();
pthread_rwlock_unlock();
pthread_rwlock_destroy();
效果展示
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<errno.h>
int count;
pthread_rwlock_t rwlock;
void*th_write(void*argv)
{
int t;
int i=(int)argv;
while(1)
{
pthread_rwlock_wrlock(&rwlock);
t=count;
usleep(2000000);
printf("------write %d:%lu:count=%d ++count=%d\n",i,pthread_self(),t,++count);
pthread_rwlock_unlock(&rwlock);
usleep(10000);
}
return NULL;
}
void*th_read(void*argv)
{
int i=(int)argv;
while(1)
{
pthread_rwlock_rdlock(&rwlock);
printf("------read %d:%lu:%d\n",i,pthread_self(),count);
pthread_rwlock_unlock(&rwlock);
usleep(20000);
}
return NULL;
}
int main()
{
int i;
pthread_t tid[8];
pthread_rwlock_init(&rwlock,NULL);
for(i=0;i<3;i++)
{
pthread_create(&tid[i],NULL,th_write,(void*)i);
}
for(i=0;i<5;i++)
{
pthread_create(&tid[i+3],NULL,th_read,(void*)i);
}
for(i=0;i<8;i++)
{
pthread_join(tid[i],NULL);
}
pthread_rwlock_destroy(&rwlock);
}
执行结果
静态初始化条件变量
条件变量本身不是锁,但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。 条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足,所以互斥锁和条件变量通常一起使用。
当条件满足的时候,线程通常解锁并等待该条件发生变化,一旦另一个线程修改了环境变量,就会通知相应的环境变量唤醒一个或者多个被这个条件变量阻塞的线程。这些被唤醒的线程将重新上锁,并测试条件是否满足。一般来说条件变量被用于线程间的同步;当条件不满足的时候,允许其中的一个执行流挂起和等待。
常用函数
pthread_cond_t cond;
pthread_cond_init();
pthread_cond_wait();
pthread_cond_timedwait();
pthread_cond_signal();
pthread_cond_broadcast();
pthread_cond_destroy();
举例样例参考 消费生产者问题
信号量
相当于初始化值为N的互斥量 。N值表示可以同时访问共享数据区的线程数
常用函数
sem_t sem;
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int sem_post(sem_t *sem);
int sem_destroy(sem_t *sem);
以上六个函数都是成功返回0,失败返回-1同时设置error 举例样例参考 消费生产者问题
|