在某个程序运行的同时系统就会创建一个进程,并且系统会给进程分配独立的地址空间,而且系统会把进程的详细信息保存在task_struct结构体中。
由于每个进程都要参与内核调度互相不影响,那么会导致进程在切换时系统开销比较大(进程的代码数据存放下内存中,CPU要访问进程就需要读取内存。一个高速设备访问一个低速设备,为了匹配就需要一个成本高容量小的cache高速缓存设备,这样就会导致进程频繁切换时系统会频繁刷新cache、TLB导致开销比较大)。因此很多操作系统引入了轻量级进程LWP(线程),同一进程中的线程共享相同的地址空间。
往期推荐 :
Linux并发程序设计(1)——进程
我还是曾今那个少年(一个普通男孩的十年)——纪念博客访问量突破十万
#include
int pthread_create(pthread_t *thread,const
pthread_attr_t *attr,void *(*routine)(void*),void *arg);
#include
int pthread_join(pthread_t thread,void **retval);
#include
void pthread_exit(void *retval);//返回的地址不能是局部变量的地址
注意: 结束线程不能用exit/_exit
,这样会使当前进程中创建的线程全部结束.
#include
#include
#include
#include
#include
#include
char message[32]="hello world";//定义一个全局变量的数组,存放在静态存储区
void *thread_func(void *arg);//声明线程开启后执行的函数
int main()
{
pthread_t a_thread;//线程对象变量
void *result;
if(pthread_create(&a_thread,NULL,thread_func,NULL)!=0){
printf("fail to pthread_create");
exit(-1);
}
printf("start\n");
pthread_join(a_thread,&result);//等待a_thread结束
printf("result is %s\n",result);
printf("message is %s\n",message);
return 0;
}
void *thread_func(void *arg)
{
sleep(1);//睡眠一秒
strcpy(message,"marked by thread");
pthread_exit("thank you for waiting for me");//退出当前线程,返回结果,
}
注意:
线程的相关函数都是通过相关的第三方库来实现的因此在使用gcc编译时要使用-l
链接选项来链接pthread,结束线程也可以使用return
。在进程中父进程结束子进程依旧可以继续运行,但是在线程中父进程(main函数)结束,子线程也随之结束。
由于线程共享同一进程的地址空间,这个地址空间的代码和全局数据每个线程都可以访问。这样就使得线程间通信很容易,可以通过全局变量来交换数据实现通信。为了避免多个线程访问全局变量时出现线程调度、时间片用完等情况造成数据的破坏,那么我们就需要同步和互斥机制来保证数据的完整。
同步(synchronization)指的是多个任务按照约定的先后次序互相配合完成一件事1968年,Edsgar Dijkstra基于信号量提出了一种同步机制,由信号量来决定线程是继续运行还是阻塞等待。
信号量代表一类资源,其值表示系统中该资源的数量,同时它也是一个受保的变量,只能通过三种操作来访问(初始化、P操作——申请资源、V操作——释放资源)。
if(信号量的值大于0){申请资源的任务继续运行;
信号量的值减一;}
else{申请资源的任务阻塞;}
信号量的值加一;
if(有任务在等待资源){唤醒等待业务,让其继续运行}
#include
int sem_init(sem_t *sem,int pshared,unsigned int value);
#include
int sem_wait(sem_t *sem);//P操作
#include
int sem_post(sem_t *sem);//V操作
注意:P操作是在任务需要访问资源之前,如果访问完资源需要通过V操作告诉系统。
#include
#include
#include
#include
#include
#include
char buf[32];//全局缓冲区
sem_t sem;
void *function(void *arg);//线程执行函数
int main()
{
pthread_t a_thread;
if(sem_init(&sem,0,0)<0){//信号量初始化
perror("sem_init");
exit(-1);
}
if(pthread_create(&a_thread,NULL,function,NULL)!=0){
printf("fail to pthread_create");
exit(-1);
}
do{
fgets(buf,32,stdin);
sem_post(&sem);//V操作,写缓冲区
}while(strncmp(buf,"quit",4)!=0);//看输入是否为quit,不是则继续
return 0;
}
void *function(void *arg)
{
while(1)
{
sem_wait(&sem);//P操作,检查缓冲区是否有数据
printf("you enter %d characters\n",strlen(buf));//操作成功完成打印结果
}
}
以上程序并不是严格意义上的同步,因为只有P操作(读缓冲区)在操作前检查缓冲区是否有数据,但是V操作(写操作)并没有检查操作前是否有缓冲数据。 因此以上代码可以进行为以下代码。
#include
#include
#include
#include
#include
#include
char buf[32];//全局缓冲区
sem_t sem_r,sem_w;//sem_r可读缓冲物的个数,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("fail to pthread_create");
exit(-1);
}
do{
sem_wait(&sem_w);//P操作,判断是否有可以写的线程
fgets(buf,32,stdin);
sem_post(&sem_r);//V操作,写缓冲区
}while(strncmp(buf,"quit",4)!=0);//看输入是否为quit,不是则继续
return 0;
}
void *function(void *arg)
{
while(1)
{
sem_wait(&sem_r);//P操作,检查缓冲区是否有数据
printf("you enter %d characters\n",strlen(buf));//操作成功完成打印结果
sem_post(&sem_w);//缓冲区可写,就通知下一次可写操作
}
}
临界资源
临界区
互斥机制
互斥锁初始化——pthread_mutex_init
#include
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_t* attr);
申请锁——pthread_mutex_lock
#include
int pthread_mutex_lock(pthread_mutex_t *mutex);
释放锁——pthread_mutex_unclock
#include
int pthread_mutex_unclock(pthread_mutex_t *mutex);
#include
#include
#include
#include
#include
unsigned int count,value1,value2;
pthread_mutex_t lock;
void *funtion(void *arg);
int main()
{
pthread_t a_thread;
if(pthread_mutex_init(&lock,NULL)!=0){//锁初始化,默认属性,只需要初始化一次
printf("fail to pthread_mutex_init\n");
exit(-1);
}
if(pthread_create(&a_thread,NULL,funtion,NULL)!=0){//创建线程
printf("fail to pthread_create");
exit(-1);
}
while(1){
count++;
#ifdef _LOCK_//条件编译
pthread_mutex_lock(&lock);
#endif
value1=count;
value2=count;
#ifdef _LOCK_
pthread_mutex_unlock(&lock);
#endif
}
return 0;
}
void *funtion(void *arg)
{
while(1){
#ifdef _LOCK_
pthread_mutex_lock(&lock);
#endif
if(value1!=value2){
printf("value1=%u,value2=%u\n",value1,value2);
usleep(100000);
}
#ifdef _LOCK_
pthread_mutex_unlock(&lock);
#endif
}
return NULL;
}
以上文件存放在Syn.c中,通过命令gcc -o syn Syn.c -lpthread -D_LOCK_
来运行代码可以发现保护了临界区的数据。
不积小流无以成江河,不积跬步无以至千里。而我想要成为万里羊,就必须坚持学习来获取更多知识,用知识来改变命运,用博客见证成长,用行动证明我在努力。
如果我的博客对你有帮助、如果你喜欢我的博客内容,记得“点赞” “评论” “收藏”一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。