效果如下图:
给大家解释一下上述效果:在左侧(顶格)的是生产者(Producer);在右侧(空格)的是消费者(Consumer)。生产者有1个,代号为“0”;消费者有2个,代号分别为“0”和“1”。
生产者首先生产出一个产品,输出“is producing Product”。然后唤醒消费者来消费,输出“is waking Consumer”。
消费者生成时会报告自己的信息,比如“I am Consumer 0”代表它是0号消费者。如果有东西可以消费,它会输出“Consumer 代号 consume product success!!!!”代表消费成功。
程序实现的效果是:生产者不断生产“产品”,然后消费者“0”和“1”不断进行消费,如此循环往复。
一、代码剖析
代码逻辑如下,很清晰:
入口函数代码逻辑:
从HxSyscall进入,调用pthread_mutex_init初始化互斥锁mutex(是一个pthread_mutex_t结构体),调用pthread_cond_init初始化条件变量has_product(是一个pthread_cond_t结构体)。
紧接着定义了一个thread_ids数组,该数组用于存储生产者和消费者的编号。
然后先调用pthread_create函数生成生产者,在这里解释一下4个传入参数各自的含义:
第1个参数是一个指向pthread_t类型变量的指针,用于存储新创建的线程,简言之就是新创建的线程会存放在该变量里。
第2个参数是一个指向pthread_attr_t类型变量的指针,用于指定新线程的属性,这里是NULL,说明用的是默认属性。
第3个参数:是一个指向函数的指针,该函数就是新线程的入口点。可以这么理解:这里传入什么函数名就去调用哪个函数。比如在这里我们将生产者producer的函数名传入,就会去执行producer函数。
第4个参数:一个指向void类型的指针,用于传递给新线程入口点函数的参数。可以这么理解:这里传入的值就是要调用函数的参数,比如我们调用producer函数需要传入了一个参数arg,这里的这第4个参数就是这个参数arg。
大家需要注意一点:如果需要在线程执行时输出语句printf(),一定要在printf()后加上fflush(stdout);这条语句,因为当我们使用printf等输出函数打印文本时,并不会立即将其发送到屏幕上,而是先存储在输出缓冲区中,而fflush(stdout)这条语句可以刷新输出缓冲区,将缓冲区内容输出到屏幕上。
最后说一下pthread_join函数的是用于阻塞调用线程,直到指定的线程结束执行,实现线程的同步,确保调用线程等待指定线程的完成。在入口函数中执行该操作,会等待所有线程都执行完之后再继续执行。简而言之就是要避免:某些线程还在运行,却意外销毁了该些线程所需的资源,导致出错的情况发生。
补充:
1.互斥锁(Mutex)的作用是保护共享资源,确保同一时间只有一个线程可以访问共享资源。
2.条件变量(Condition Variable)的作用是实现线程间的协调和通信。条件变量允许一个线程等待某个条件的发生,并在条件满足时被唤醒。
消费者函数逻辑:
用pthread_mutex_lock来上互斥锁,pthread_mutex_unlock来解除互斥锁,在此不过多赘述。
当ready==0时说明此时消费者生产的东西都被消费完了,或者还没来得及生产,因此用while循环来忙等待。
pthread_cond_wait函数的作用是使线程等待某个条件的发生,一旦条件满足(收到信号),线程将被唤醒并继续执行。在这里等待的是has_product变量。
当ready>0时,令ready-1表示消费掉1个产品,然后休眠3秒
生产者函数逻辑:
用pthread_mutex_lock来上互斥锁,pthread_mutex_unlock来解除互斥锁,在此不过多赘述。
pthread_cond_signal用于向线程发送信号,告诉它们条件已经满足,你可以执行啦!与消费者中的pthread_cond_wait函数相呼应。
二、可运行代码
代码如下可直接复制:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <pthread.h>#define CONSUMER_NUM 2#define PRODUCER_NUM 1pthread_t pids[CONSUMER_NUM+PRODUCER_NUM];int ready = 0;
int running =1;pthread_mutex_t mutex;pthread_cond_t has_product;void* producer(void* arg){int no = (int)arg;while(running){pthread_mutex_lock(&mutex);ready++;printf("Producer %d is producing Product\n",no);fflush(stdout); pthread_cond_signal(&has_product);printf("Producer %d is waking Consumer\n",no);fflush(stdout); pthread_mutex_unlock(&mutex);sleep(1);}
return NULL;
}void* consumer(void* arg){int num = (int)arg;while(running){pthread_mutex_lock(&mutex); while(ready==0){printf("\tConsumer %d is waiting...\n",num);fflush(stdout); pthread_cond_wait(&has_product,&mutex);}ready--;printf("\tConsumer %d consume product success!!!!!\n",num);fflush(stdout); pthread_mutex_unlock(&mutex);sleep(3);}
return NULL;
}void HxSyscall(int num){ pthread_mutex_init(&mutex,NULL);pthread_cond_init(&has_product,NULL);printf("init success!\n");int i;int thread_ids[CONSUMER_NUM + PRODUCER_NUM]; for(i=0; i<PRODUCER_NUM; i++){thread_ids[i] = i;pthread_create(&pids[i], NULL, producer, (void*)i);}for(i=0; i<CONSUMER_NUM; i++){printf("\tI am Consumer %d \n",i);fflush(stdout); sleep(2);thread_ids[PRODUCER_NUM + i] = i;pthread_create(&pids[PRODUCER_NUM + i], NULL, consumer, (void*)i);}for(i=0; i<PRODUCER_NUM + CONSUMER_NUM; i++){pthread_join(pids[i], NULL);}pthread_mutex_destroy(&mutex);pthread_cond_destroy(&has_product);return;
}
大家只需要按照project1的方式,将上述代码放入home/openharmony/kernel/liteos_a/syscall下的hx_syscall.c文件夹下即可(这里为了方便基础较薄弱的同学操作,所以我们仍旧采用勖哥在pro1中的函数命名),接下来大家只需要按照pro1的方式进行编译烧录即可运行。
【如果觉得有帮助记得点赞+收藏⭐】