线程是共享相同地址空间的多个任务。所以线程之间通信可以利用 静态数据(全局变量)作为中介来实现。前面关于线程的创建中有给出例子。但是线程与主线程之间的执行顺序这个无法确定,主要看在时间片中能执行多少指令,也可以使用sleep()函数来延缓下面的指令的执行。
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
char a[64]="I love you!";
void *fuc()
{
sleep(1);
stpcpy(a,"I hate you !");
pthread_exit("come from pthread_exit");
}
int main()
{
pthread_t a_thread;
void *result;
if((pthread_create(&a_thread,NULL,&fuc,NULL))!=0)
{
printf("pthread_creat is failed!\n");
exit(-1);
}
printf("%s\n",a); //此时a里面数据还未被线程修改,因为fuc里面有sleep
sleep(3);
printf("%s\n",a); //此时a里面的数据已经被修改,因为主线程sleep 3 a线程已经执行完
stpcpy(a,"I love you,too!");
printf("%s\n",a);
pthread_join(a_thread,&result);
printf("%s\n",(char*)result);
return 0;
}
最后的结果如图:
由于线程之间的执行顺序不确定性较大,为了消除不确定性导致的问题,引入了同步机制和互斥机制。
同步机制,是指多个任务按照约定的先后顺序相互配合完成一件事。为了执行同步机制还引入了信号量的P,V操作,P申请资源,V释放资源。
比如经典的生产者与消费者问题,读写问题,必须要在buf中写入数据之后才能进行读,在读的过程中不能进行写操作。
#include<stdio.h>
#include<semaphore.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
char buf[32];
sem_t sem_r; //可读缓冲区的个数
sem_t sem_w; //可写缓冲区的个数
void *function(void *arg);
int main()
{
pthread_t a_thread;
if(sem_init(&sem_r,0,0)<0){ //初始化信号量,刚开始可读的资源为0
perror("sem_init");
exit(-1);
}
if(sem_init(&sem_w,0,1)<0){ //初始化信号量,刚开始可写的资源为1
perror("sem_init");
exit(-1);
}
if(pthread_create(&a_thread,NULL,&function,NULL)!=0) //需要先初始化信号量再创线程,因为
{ //如果先创线程再初始化可能导致初始化两遍信号量
printf("pthread_create is failed!\n");
exit(-1);
}
printf("input 'quit' to exit\n");
do{
sem_wait(&sem_w); //写之前先申请写资源 P(w)
fgets(buf,32,stdin);
sem_post(&sem_r); //写之后释放资源增加可读资源 v(r)
}while(strncmp(buf,"quit",4)!=0);
return 0;
}
void *function(void *arg)
{
while(1)
{
sem_wait(&sem_r); //读之前申请读的资源 p(r)
printf("you enter %ld characters\n",strlen(buf));
sem_post(&sem_w); //读之后释放资源 增加可写的资源 v(w)
}
}
互斥机制,使用mutex互斥锁,在访问临界资源时申请锁,访问完解锁。
当pv操作中的信号量只有01时也相当于互斥锁。
总结:
线程由于共享相同地址空间,所以通信很方便,但也容易由执行顺序导致通信出错。
引入pv操作和互斥锁来确保数据的正确性。