在系统里面线程的资源不同于进程的资源,进程在系统里面是一个一个的单独个体,但是线程的资源大部分是共享的,所以在线程访问问一个临界资源的时候需要同步与互斥机制,从而保证在多线程的程序里main不会出现问题。
模拟实现售票系统:
int tacket = 100;
void* sell_tacket(void* arg){
while(1){
if(tacket > 0){
usleep(10000);
printf("%u id sell tacket :%d\n",pthread_self(),tacket);
tacket--;
}
else
break;
}
}
int main(){
pthread_t tid1,tid2,tid3,tid4;
pthread_create(&tid1,NULL,sell_tacket,NULL);
pthread_create(&tid2,NULL,sell_tacket,NULL);
pthread_create(&tid3,NULL,sell_tacket,NULL);
pthread_create(&tid4,NULL,sell_tacket,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);
return 0;
}
原因是在每个线程在sleep时候很有可能被切换出去,那么其他线程就会执行自己的代码,所以在到最后面的时候会出现票数为负数的情况。
互斥量
类型:pthread_mutex_t
:定义互斥量可以pthread_mutex_t lock;
互斥量的初始化:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
或者
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
使用PTHREAD_MUTEX_INITIALIZER
初始化互斥量一般是初始化全局的互斥量。
互斥量的销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
mutex:已初始化的互斥量
注:
加锁和解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
所以售票系统可以改进为:
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int tacket = 100;
void* sell_tacket(void* arg){
pthread_mutex_lock(&lock);
while(1){
if(tacket > 0){
usleep(10000);
printf("%u id sell tacket :%d\n",pthread_self(),tacket);
tacket--;
}
else{
pthread_mutex_unlock(&lock);
break;
}
}
pthread_mutex_unlock(&lock);
}
int main(){
pthread_t tid1,tid2,tid3,tid4;
pthread_create(&tid1,NULL,sell_tacket,NULL);
pthread_create(&tid2,NULL,sell_tacket,NULL);
pthread_create(&tid3,NULL,sell_tacket,NULL);
pthread_create(&tid4,NULL,sell_tacket,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);
return 0;
}
但是这里有一个缺点就是,在有一个线程再购票的时候其他的线程是无法取得购票权的。
条件变量
条件变量的类型:pthread_cond_t
定义条件变量可以:pthread_cond_t cond
条件变量的初始化:
int pthread_cond_init(pthread_cond_t * cond,const pthread_condattr_t *attr);
或者
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
条件变量的销毁:
int pthread_cond_destroy(pthread_cond_t *cond);
唤醒等待:
int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒多个
int pthread_cond_signal(pthread_cond_t *cond);//唤醒一个
改进售票系统:一个线程只能够买一张票
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond;
int tacket = 100;
void* sell_tacket(void* arg){
pthread_mutex_lock(&lock);
if(tacket > 0){
pthread_cond_wait(&cond,&lock);
printf("%u id sell tacket :%d\n",pthread_self(),tacket);
tacket--;
}
pthread_mutex_unlock(&lock);
}
int main(){
int i = 0;
int j = 0;
pthread_t tid1,tid2,tid3,tid4;
pthread_cond_init(&cond,NULL);
pthread_create(&tid1,NULL,sell_tacket,NULL);
pthread_create(&tid2,NULL,sell_tacket,NULL);
pthread_create(&tid3,NULL,sell_tacket,NULL);
pthread_create(&tid4,NULL,sell_tacket,NULL);
sleep(1);
for(;i<4;i++){
usleep(1000);
pthread_cond_signal(&cond);
}
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);
return 0;
}
注意:
由于解锁和等待不是原子操作,所以在解锁之后到等待之前有其他的线程满足条件,发送了信号,这个时候原线程将会错过这个信号,那么有可能该线程将一直等待下去。
在使用条件变量的时候应该保证解锁和等待是一个原子操作。