尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux篇——线程间通信

Linux篇——线程间通信 一、互斥锁1、定义一个锁pthread_mutex_t mutex;2、初始化锁形式int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);功能将已经定义好的互斥锁初始化。参数mutex 要初始化的互斥锁atrr是初始化的值一般是NULL表示默认锁。返回值成功返回0失败返回非零pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; //静态初始化3、加锁形式int pthread_mutex_lock(pthread_mutex_t *mutex);功能用指定的互斥锁开始加锁代码加锁后的代码到解锁部分的代码属于原子操作不可分割的操作要么不做要么一次做完在加锁期间其他进程/线程都不能操作该部分代码如果该函数在执行的时候mutex已经被其他部分使用则代码阻塞。参数 mutex 用来给代码加锁的互斥锁返回值成功返回0失败返回非零。4、解锁形式int pthread_mutex_unlock(pthread_mutex_t *mutex);功能将指定的互斥锁解锁解锁之后代码不再排他访问一般加锁解锁同时出现。参数用来解锁的互斥锁返回值成功返回0失败返回非零5、销毁形式int pthread_mutex_destroy(pthread_mutex_t *mutex);功能使用互斥锁完毕后需要销毁互斥锁参数mutex 要销毁的互斥锁返回值成功返回0失败返回非零6、trylock形式int pthread_mutex_trylock(pthread_mutex_t *mutex);功能类似加锁函数效果唯一区别就是不阻塞。参数mutex 用来加锁的互斥锁返回值成功返回0失败返回非零E_AGAIN例子两个进程分别对cnt加50000次在没上锁的时候代码运行到temptemp1;时这个时间片可能就结束了在另一个线程中又拿着上一次的cnt给temp赋值。举个例子就是现在cnt100在第一个进程里面tempcnt100temptemp1101假设现在时间片结束进入第二个进程此时cnt100tempcnt100temptemp1101cnttemp101n--然后回到第一个进程再第一个进程里面的temp还是101所以cnt101.这就相当于两个进程都走了一次但是数值只加了1所以必须加上互斥锁才能得到正确的100000。#include stdio.h #include pthread.h #include errno.h #include unistd.h int cnt 0; pthread_mutex_t mutex; //创建了互斥锁变量 void *do_sth1(void *arg) { int n 50000; while (n) { //pthread_mutex_lock(mutex); int ret 0; while ((ret pthread_mutex_trylock(mutex)) ! 0) { errno ret; perror(1 try lock); } int temp cnt; printf(1cnt %d\n,cnt); temp temp 1; cnt temp; --n; pthread_mutex_unlock(mutex); } return NULL; } void *do_sth2(void *arg) { int n 50000; while (n) { //pthread_mutex_lock(mutex); int ret 0; while ((ret pthread_mutex_trylock(mutex)) ! 0) { errno ret; perror(2 try lock); } int temp cnt; printf(2cnt %d\n,cnt); temp temp 1; cnt temp; --n; pthread_mutex_unlock(mutex); } return NULL; } int main(int argc, const char *argv[]) { pthread_mutex_init(mutex,NULL);//初始化这把锁 pthread_t tid[2]; int ret pthread_create(tid[0],NULL,do_sth1,NULL); if (ret ! 0) { errno ret; perror(pthread_create fail); return -1; } ret pthread_create(tid[1],NULL,do_sth2,NULL); if (ret ! 0) { errno ret; perror(pthread_create fail); return -1; } pthread_join(tid[0],NULL); pthread_join(tid[1],NULL); pthread_mutex_destroy(mutex); //销毁锁 return 0; }二、信号量在这部分我们要注意的就是这里的操作都是基于收到了一个什么信号然后做出相应的行为所以我们需要开两个终端一个用于运行代码另一个来给他发信号。a||---------[]------------b||以十字路口为例只有当这条路上的灯为绿灯时这条路才能通行那么信号量也是这种道理只有我等的这个信号量有时才会执行它下面的代码。a.信号量的定义 sem_t sem;信号量的类型 信号量的变量sem_t sem_w;一般定义为写资源sem_t sem_r;一般定义为读资源b.信号量的初始化int sem_init(sem_t *sem, int pshared, unsigned int value);功能将已经定义好的信号量赋值。参数sem 要初始化的信号量pshared 0 表示线程间使用信号量!0 ;表示进程间使用信号量value 信号量的初始值一般无名信号量都是二值信号量0 1 0表示红灯即没有信号量进程暂停阻塞1表示绿灯即有信号量进程可以通过执行。返回值成功时返回0失败时返回 -1egsem_init(sem_w,0,1); 表示我有一个写资源可用sem_init(sem_r,0,0); 表示当前没有读资源c.信号量的PV 操作P 》申请资源》申请一个二值信号量 》sem_wait();V 》释放资源》释放一个二值信号量》sem_post();int sem_wait(sem_t *sem); p操作是消费资源因为他要用信号量功能判断当前sem信号量是否有资源可用如果sem有资源(1)则申请该资源(对应的资源数量要减1)程序继续运行如果sem没有资源(0)则线程阻塞等待一旦有资源则自动申请资源并继续运行程序。P操作的逻辑先去申请资源有就拿来用对应资源数量减1没有就阻塞直到有资源可用注意sem 申请资源后会自动执行 sem sem - 1;参数sem 要判断的信号量资源返回值成功返回0失败返回-1int sem_post(sem_t *sem); V操作生产资源功能函数可以将指定的sem信号量资源释放 (释放资源表示产生了一个对应的资源 对应资源的数量加1)并默认执行sem sem1;线程在该函数上不会阻塞。(V的操作逻辑--释放资源如果有线程要用则拿去使用如果没有线程用则对应资源数量加1)参数sem 要释放资源的信号量返回值成功时返回0失败返回-1d.信号量的销毁int sem_destroy(sem_t *sem);功能使用完毕将指定的信号量销毁参数sem要销毁的信号量返回值成功时返回0失败时返回 -1例子使用同步实现如下 [生产者-消费者模型]:排队吃饭有3张桌子,吃饭的人需要排队,等到有空桌子时,才能吃饭,请描述整个过程#include stdio.h #include unistd.h #include errno.h #include pthread.h #include semaphore.h #include stdlib.h #include string.h #define MAX_SIZE 10 typedef struct { char name[MAX_SIZE][100]; //登记用餐的人名 int total_num; //登记用餐总人数 int call_num; //当前叫号 }dinner_t; dinner_t d {}; sem_t sem_d; pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; void * do_eat(void *arg) { int i *(int *)arg; pthread_mutex_unlock(mutex); sleep(1); printf(\n-%d:%s eat...\n,i,d.name[i]); srand(time(NULL)); sleep(rand()%10); //rand()%101; // 0 ~ 9 printf(----%s out-\n,d.name[i]); sem_post(sem_d); } void *do_waiter(void *arg) { while(1) { sem_wait(sem_d);//申请资源 if (d.call_num d.total_num)//有人 call total { pthread_t tid; pthread_mutex_lock(mutex); int ret pthread_create(tid,NULL,do_eat,d.call_num); if (ret!0) { pthread_mutex_unlock(mutex); errno ret; perror(pthread_create fail); return NULL; } pthread_detach(tid); //设置为分离 ---系统自动回收资源 pthread_mutex_lock(mutex); d.call_num (d.call_num1) % MAX_SIZE; pthread_mutex_unlock(mutex); } else { sem_post(sem_d);//申请资源 } } return NULL; } int main(int argc, const char *argv[]) { //前台 pthread_t tid; int ret pthread_create(tid,NULL,do_waiter,NULL); if (ret ! 0) { errno ret; perror(pthread_create fail); return -1; } sem_init(sem_d,0,3); int i 0; while (1) { printf(Input dinner user name:); fgets(d.name[i],sizeof(d.name[i]),stdin); d.name[i][strlen(d.name[i])-1] \0; d.total_num; i (i1)%MAX_SIZE; } return 0; }这里要注意的就是在整个过程中是会有人不断来的只有在上一桌人吃完释放信号量之后才证明有新的位置可以让其他顾客进入当然这里也是不够完善的例如谁在哪个桌子吃饭以及退出如果需要知道在哪个桌子吃饭只需要设置三个不同的信号量就可以区分退出可以用之前提过的alarm函数设置工作多久就停止。
返回列表