距离上一次利用高并发技术实现360度行车记录仪功能已经过去半年了。开始写一系列关于系统编程和网络编程内容进行总结。
温故而知新,欢迎大家讨论学习。
2021-09-08 复习内容
:
- 复习代码
- 1 man 1 man 2 man 3 分别是标准命令 系统调用 和 库函数
- 编译需要 -l pthread 加载第三方库
- 什么叫第三方库呢:除本地类库、系统类库以外的类库,需要后来安装,才能调用的类库。C++常见第三方库参考
- 信号量那边的代码 如果是多生产者 都消费者就是错误的,因为没有处理生产者和生产者 或者 消费者和消费者之间互斥访问的问题。可能存在同时进入临界区。
- sem_init 初始化 分线程间 参数为0 进程间 参数为1 注意
2021-10-06 补充内容
- 条件变量虚假唤醒的问题
- 条件变量也叫做管程
- 生产者生产 上锁 生产 解锁 唤醒 ;消费者消费 上锁 while 判断阻塞 消费 释放锁。
举个例子:
两个线程都把全局变量增加1,这个操作平台需要三条指令完成
从内存读到寄存器 →寄存器的值加1 →将寄存器的值写会内存。
如果此时线程A取值在寄存器修改还未写入内存,线程B就从内存取值就会导致两次操作实际上只修改过一次。或者说后一次线程做的事情覆盖前一次线程做的事情。实际上就执行过一次线程。
作用:
初始化一个互斥锁—> 初值可看作 1
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr)
pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER;
作用:
销毁一个互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex)
作用:
加锁。可理解为将 mutex–(或 -1),操作后 mutex 的值为 0。
int pthread_mutex_lock(pthread_mutex_t *mutex);
作用:
解锁。可理解为将 mutex ++(或 +1),操作后 mutex 的值为 1。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
作用:
尝试加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);
mutex--
。而 unlock 则将 mutex++
。这部分内容在项目中没有使用过,如有需要,下次补充…
作用:
初始化一个条件变量
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict
attr);静态初始化:
pthread_cond_t cond=PTHREAD_COND_INITIALIZER
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict
attr);作用:
销毁一个条件变量
nt pthread_cond_destroy(pthread_cond_t *cond);
作用:(非常重要 三点)
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
1.2.两步为一个原子操作。
作用:
限时等待一个条件变量
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
作用:
唤醒至少一个阻塞在条件变量上的线程
int pthread_cond_signal(pthread_cond_t *cond);
作用:
唤醒全部阻塞在条件变量上的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
代码不难,就是实现一个生产消费。如果没有商品了,消费者需要去等待。
要求能够自助完整写完。
//链表是新生产的挂载头部 消费也从头部消费
//当然也可以 初始化一个哨兵头结点 新的节点挂载尾巴 一个temp(初始化为哨兵头结点) 不断指向新节点的上一个 如果temp =head 表示位空也可以。
// head = temp
// temp->next =新结点
// temp =temp->next
//每次消费 就是 temp-> 估计这边还要一个双向链表 要不找不到上一个temp 所以还是挂在头结点好一点
# include
# include
# include
//静态的方式定义了一个互斥锁 和 一个条件变量
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_product=PTHREAD_COND_INITIALIZER;
struct msg
{
struct msg* next;//指针域
int num;//数据域
};
struct msg* head;//全局头指针
void * producer(void* p)
{
struct msg* mp;//创建一个节点指针 毕竟生产 要加入到链表
while(1)
{
mp=(struct msg*)malloc(sizeof(struct msg));s
mp->num = rand()%1000+1;//放入一个1-1000的数
printf("produce is %d\n",mp->num);
pthread_mutex_lock(&lock);
mp->next=head;//先看一一句 head作为上一个节点 当前节点挂载链表头部
head=mp;//head变量保存头指针
pthread_mutex_unlock(&lock);
//唤醒消费者
pthread_cond_signal(&has_product);
sleep(rand()%5);
}
}
void* consumer(void* p)
{
struct msg* mp;
while(1)
{
pthread_mutex_lock(&lock);
while(head==NULL)
{
//空的时候 阻塞等待生产者
pthread_cond_wait(&has_product,&lock);
}
mp=head; //消费 取出头指针
head=mp->next; //头指针后移
pthread_mutex_unlock(&lock);
printf("Consume is %d\n",mp->num);
free(mp);//记得即使清空
sleep(rand()%5);
}
}
int main()
{
pthread_t pid,cid;
//创建两个线程 执行生产者和消费者
pthread_create(&pid,NULL,producer,NULL);
pthread_create(&cid,NULL,consumer,NULL);
//等待线程结束,回收 无传出值 也是防止主线程提前退出main空间
pthread_join(pid,NULL);
pthread_join(cid,NULL);
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&has_product);
return 0;
}
再三重复,条件变量不是锁,但是常常配合锁使用。
作用:
初始化一个信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
信号量的初值,决定了占用信号量的线程(进程)的个数。
作用:
销毁一个信号量
int sem_destroy (sem_t *sem);
作用:
给信号量加锁 –
int sem_wait(sem_t *sem);
作用:
给信号量解锁 ++
int sem_post(sem_t *sem);
作用:
尝试对信号量加锁 – (与 sem_wait 的区别类比 lock 和 trylock)
int sem_trywait(sem_t *sem);
作用:
限时尝试对信号量加锁 –
根据注释,代码还是比较好理解的。两个信号量来控制,一个初始为0,表示一开始生产的数量为0.另外一个初始的数量是可以存放商品的空格数,初始化就是格子数N。
要求直接写出下面代码。
补充 0425:互斥和条件变量的方式是我消费 你不能生产。 而两个信号量是你生产 我可以消费。毕竟环形的 不影响,最多你生产了,我不知道。就怕多个生产者 ,你格子放入了还没移到下一个格子我又重复放入了。
# include
# include
# include
# include
# define N 5
int queue[N];
sem_t blank_number,product_number;//两个信号量控制
void * producer(void*arg)//生产
{
int p=0;
while(1)
{
sem_wait(&blank_number);//空格的位置-1
queue[p]=rand()%1000+1;//放入队列
printf("product %d \n",queue[p]);
sem_post(&product_number);//生产的数量+1
p=(p+1)%N;//1-N 1-N 循环放入 模拟队列
sleep(rand()%5);
}
}
void * consumer(void* arg)//消费者
{
int c=0;
while(1)
{
sem_wait(&product_number);//产品数量-1
printf("Consume %d\n",queue[c]);
queue[c]=0;
sem_post(&blank_number);//空格数量+1
c=(c+1)%N;
sleep(rand()%5);
}
}
int main()
{
sem_init(&blank_number,0,N);//空格的数量(剩余空间的位置)
sem_init(&product_number,0,0);//已经生产的数量
pthread_t pid,cid;
pthread_create(&pid,NULL,producer,NULL);
pthread_create(&cid,NULL,consumer,NULL);
pthread_join(pid,NULL);
pthread_join(cid,NULL);
}
异步处理就是,你现在问我问题,我可以不回答你,等我用时间了再处理你这个问题.同步不就反之了,同步信息被立即处理 – 直到信息处理完成才返回消息句柄;异步信息收到后将在后台处理一段时间 – 而早在信息处理结束前就返回消息句柄。
参考链接
如有错误欢迎指出…