线程同步就是当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。实现线程同步的方法有互斥量、信号量、条件变量和读写锁。
互斥量(互斥锁)
基本实现就是在线程进入临界区之前,执行加锁操作;线程退出之后,执行解锁操作。
互斥锁类型:pthread_mutex_t //一般定义在全局
互斥锁操作有初始化、加锁、解锁以及销毁,具体如下:
int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr_t *attr);//初始化
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<stdlib.h>
#include<pthread.h>
#include<assert.h>
#include<time.h>
pthread_mutex_t mutex;
char buff[128]={0};
void *fun(void* arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
if(strncmp(buff,"end",3)==0)
{
break;
}
printf("fun:%s\n",buff);
memset(buff,0,128);
int n=rand()%3+1;
sleep(n);
pthread_mutex_unlock(&mutex);
n=rand()%3+1;
sleep(n);
}
}
int main()
{
srand((unsigned int)(time(NULL)*time(NULL)));
pthread_t id;
pthread_mutex_init(&mutex,NULL);
int res=pthread_create(&id,NULL,fun,NULL);
assert(res==0);
while(1)
{
pthread_mutex_lock(&mutex);
printf("input:");
fgets(buff,127,stdin);
pthread_mutex_unlock(&mutex);
if(strncmp(buff,"end",3)==0)
{
break;
}`在这里插入代码片`
int n=rand()%3+1;
sleep(n);
}
pthread_join(id,NULL);
pthread_mutex_destroy(&mutex);
exit(0);
}
信号量
一种特殊的计数器。
信号量类型: sem_t //全局变量
int sem_init(sem_t *sem);//初始化
int sem_wait(sem_t *sem);//P操作
int sem_post(sem_t *sem);//V操作
int sem_destroy(sem_t *sem);//销毁
使用例子:
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<semaphore.h>
#include<stdlib.h>
include<pthread.h>
sem_t sem1;
sem_t sem2;
char buff[128]={0};
void* fun(void *arg)
{
while(1)
{
sem_wait(&sem1);
if(strncmp(buff,"end",3)==0)
{
break;
}
printf("fun:%s\n",buff);
memset(buff,0,128);
sem_post(&sem2);
}
}
int main()
{
sem_init(&sem1,0,0);
sem_init(&sem2,0,1);
pthread_t id;
int res=pthread_create(&id,NULL,fun,NULL);
assert(res==0);
while(1)
{
sem_wait(&sem2);
printf("input:");
fgets(buff,127,stdin);
sem_post(&sem1);//主线程唤醒函数线程
if(strncmp(buff,"end",3)==0)
{
break;
}
}
pthread_join(id,NULL);
sem_destroy(&sem1);
sem_destroy(&sem2);
exit(0);
}
条件变量
使用方法:
int pthread_cond_init(pthread_cond_t *cong,pthread_condattr_t *attr);//初始化
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
int pthread_cond_signal(pthread_cond_t *cond);//唤醒单个线程
int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒多个线程
int pthread_cond_destroy(pthread_cond_t *cond);
传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作时原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wiat返回时,互斥量再次被锁住。
使用例子:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<pthread.h>
char buff[128]={0};
pthread_cond_t cond;
pthread_mutex_t mutex;
void *fun(void *arg)
{
char*s =(char*)arg;
while(1)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
printf("%s:%s",s,buff);
if(strncmp(buff,"end",3)==0)
{
break;
}
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
pthread_t id[2];
int res=pthread_create(&id[0],NULL,fun,"thread1");
assert(res==0);
int res2=pthread_create(&id[1],NULL,fun,"thread2");
assert(res2==0);
while(1)
{
printf("input: ");
fgets(buff,127,stdin);
if(strncmp(buff,"end",3)==0)//唤醒在cond上等待的所有线程
{
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
break;
}
else//唤醒其中一个线程
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}
pthread_join(id[0],NULL);
pthread_join(id[1],NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
读写锁
有关操作如下:
int pthread_rwlock_init(pthread_rwlock_t *rwlock,pthread_rwlockattr_*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_destroy(pthread_rwlock_t *rwlock);