线程竞争
在多线程的环境里,如果共享资源没有上锁,将会出现共享数据混乱的情况。
在单核CPU系统里,系统以时间片调度的方式让多个程序轮流使用处理器,造成了【并发】的假象。
线程同步
互斥量
int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
//亦可 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_lock (pthread_mutex_t* mutex);
int pthread_mutex_unlock (pthread_mutex_t* mutex);
int pthread_mutex_destroy (pthread_mutex_t* mutex);
保证同一时间只有一个线程可以对共享资源进行操作,但是不保证同步
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int n = 0;
pthread_mutex_t lock;
void *add(void *arg){
int i;
for(i=0;i<1000000000;i++){
pthread_mutex_lock(&lock);
++n;
pthread_mutex_unlock(&lock);
}
return NULL;
}
int main(){
pthread_mutex_init(&lock,NULL);
pthread_t id1,id2;
int ret = pthread_create(&id1,NULL,add,NULL);
ret = pthread_create(&id2,NULL,add,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_mutex_destroy(&lock);
printf("n = %d\n",n);
return 0;
}
当不上锁的时候 上锁
信号量
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。
以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。 在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
int n = 0;
sem_t sem;
void *add(void *arg){
int i;
for(i=0;i<1000000000;i++){
sem_wait(&sem);
++n;
sem_post(&sem);
}
}
int main(){
pthread_t ids[2];
int i,ret;
ret = sem_init(&sem,0,1);
if(ret == -1){
perror("sem_init");
return -1;
}
for(i=0;i<2;i++){
ret = pthread_create(&ids[i],NULL,add,NULL);
if(ret != 0){
printf("%s\n",strerror(ret));
return -1;
}
}
for(i=0;i<2;i++)
pthread_join(ids[i],NULL);
printf("n = %d\n",n);
sem_destroy(&sem);
return 0;
}
条件变量
功能:可以更好的协调多个线程工作,使cpu不会盲目调度,需要执行的线程收到信号后则执行,收不到信号的则睡眠等待信号。 通常需要合互斥量配合使用。互斥量保证同一时间只能有一个线程工作,条件变量能指定某个线程工作
步骤:
1.对竞争部分加互斥锁 pthread_mutex_lock(&mm);
2.用while循环判断标志位,保证线程是收到信号后被唤醒的
while(flag!=0) {pthread_cond_wait(&no_ok,&mm); }
操作函数部分
3.更改标志位 :flag
4.解锁 pthread_mutex_unlock(&mm);
5.发送信号:pthread_cond_signal() 或 pthread_cond_broadcast()
通过缓冲区隔离生产者和消费者,与二者直连相比,避免相互等待,提高运行效率。 生产快于消费,缓冲区满,撑死。 消费快于生产,缓冲区空,饿死。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <stdbool.h>
#include <time.h>
#define MAX_SIZE 10
char vect[MAX_SIZE];
int size = 0;
pthread_mutex_t lock;
pthread_cond_t notfull;
pthread_cond_t notempty;
void show(const char *pillow,char data){
int i;
for(i=0;i<size;i++){
printf("%c ",vect[i]);
}
printf("%s [%c] %lu\n",pillow,data,pthread_self());
}
void *producer(void *arg){
while(true){
pthread_mutex_lock(&lock);
while(size >= MAX_SIZE){
pthread_cond_wait(¬full,&lock);
}
char data = rand()%26+'A';
show("<---",data);
vect[size++] = data;
pthread_cond_broadcast(¬empty);
pthread_mutex_unlock(&lock);
usleep(rand()%100*1000);
}
return NULL;
}
void *customer(void *arg){
while(true){
pthread_mutex_lock(&lock);
while(size == 0){
pthread_cond_wait(¬empty,&lock);
}
char data = vect[--size];
show("--->",data);
pthread_cond_broadcast(¬full);
pthread_mutex_unlock(&lock);
usleep(rand()%100*1000);
}
return NULL;
}
int main(){
srand(time(NULL));
int ret = 0;
ret = pthread_mutex_init(&lock,NULL);
ret = pthread_cond_init(¬full,NULL);
ret = pthread_cond_init(¬empty,NULL);
pthread_t ids[6];
int i;
for(i=0;i<3;i++)
pthread_create(&ids[i],NULL,producer,NULL);
for(;i<6;i++)
pthread_create(&ids[i],NULL,customer,NULL);
for(i=0;i<6;i++)
pthread_join(ids[i],NULL);
pthread_mutex_destroy(&lock);
pthread_cond_destroy(¬full);
pthread_cond_destroy(¬empty);
return 0;
}
|