一、线程概述
cpp
复制代码
线程概念:
进程:有独立的进程地址空间。有独立的pcb。 分配资源的最小单位
线程:有独立的pcb。没有独立的进程地址空间。 cpu执行的最小单位。
ps -Lf 进程id:可以查看该进程的全部线程
cpp
复制代码
1.轻量级进程(light-weight process),也有PCB,创建线程使用的底层函数和进程一样,都是clone
2.从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表是相同的。
3.进程可以蜕变成线程。
4.线程可看做寄存器和栈的集合
cpp
复制代码
线程共享资源:
1.文件描述符表(可用于通信)
2.每种信号的处理方式(哪个线程先抢到信号就优先接收)
3.当前工作目录。
4.用户ID和组ID。
5.内存地址空间(.text/.data/.bss/heap/共享库)(共享全局变量)
线程非共享资源:
1.线程id
2.处理器现场和栈指针(内核栈)。
3.独立的栈空间(用户空间栈)。
4.errno变量
5.信号屏蔽字。
6.调度优先级
优点:1.提高程序并发性 2.开销小 3.数据通信、共享数据方便
缺点:1.库函数,不稳定 2.调试、编写困难、gdb不支持 3.对信号支持不好。
二、创建线程
cpp
复制代码
pthread_t pthread_self(void);
获取线程id。线程id是在进程地址空间内部,用来标识线程身份的id号。
返回值:本线程id
int pthread_create(pthread_t *tid, const pthread_attr_t *attr,
void *(*start_rountn)(void *), void *arg);
参1:传出参数,表示新创建的子线程id
参2:线程属性。传NULL表使用默认属性。
参3:子线程回调函数。创建成功,ptherad_create函数返回时,该函数会被自动调用。
参4:参3的参数。没有的话,传NULL
返回值:成功:0 失败:errno
cpp
复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
void sys_err(const char* str) {
perror(str);
exit(1);
}
void* tfn(void* arg) { //子线程
printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());
return NULL;
}
int main(int argc, char* argv[]) {
pthread_t tid;
//主线程
printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self()); //l-long u-unsigned
int ret = pthread_create(&tid, NULL, tfn, NULL);
if (ret != 0) {
perror("pthread_create error");
}
sleep(1); //防止进程先结束,导致子线程没有地址空间
return 0;
}
三、循环创建多个子线程
cpp
复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
void sys_err(const char* str) {
perror(str);
exit(1);
}
void* tfn(void* arg) {
int i = (int)arg;
sleep(i); //确保有序
printf("---I'm %dth thread: pid = %d, tid = %lu\n", i+1, getpid(), pthread_self());
return NULL;
}
int main(int argc, char* argv[]) {
int i;
int ret;
pthread_t tid;
for (i = 0; i < 5; i++) {
ret = pthread_create(&tid, NULL, tfn, (void*)i); //传参要用值传递,不能地址
if (ret != 0) {
sys_err("pthread_create error");
}
}
sleep(i);
printf("main: I'm main, pid = %d, tid = %lu\n", getpid(), pthread_self());
return 0;
}
四、线程间全局变量共享
cpp
复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
int var = 100;
void* tfn(void* arg) {
var = 200;
printf("thread, var = %d\n", var);
return NULL;
}
int main(int argc, char* argv[]) {
printf("At first var = %d\n", var);
pthread_t tid;
pthread_create(&tid, NULL, tfn, NULL);
sleep(1);
printf("After pthread_create, var = %d\n", var);
return 0;
}
//主子线程共享全局变量
五、pthread_exit退出线程
cpp
复制代码
void pthread_exit(void* retval):退出当前线程。
retval:退出值。无退出值时,NULL
exit0:退出当前进程。
return:返回到调用者那里去。
pthread_exitO:退出当前线程。
如果在主线程return,就会返回到执行的总程序,从而退出整个进程
cpp
复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
void sys_err(const char* str) {
perror(str);
exit(1);
}
void* tfn(void* arg) {
int i = (int)arg;
sleep(i); //确保有序
if (i == 2) { // 第三个线程退出
//exit(0); // 表示退出进程,把整个大程序都退出了
//return NULL; // 表示返回到函数调用者那里去,可以退出线程
pthread_exit(NULL);
}
printf("---I'm %dth thread: pid = %d, tid = %lu\n", i + 1, getpid(), pthread_self());
return NULL;
}
int main(int argc, char* argv[]) {
int i;
int ret;
pthread_t tid;
for (i = 0; i < 5; i++) {
ret = pthread_create(&tid, NULL, tfn, (void*)i); //传参要用值传递,不能地址
if (ret != 0) {
sys_err("pthread_create error");
}
}
sleep(i);
printf("main: I'm main, pid = %d, tid = %lu\n", getpid(), pthread_self());
return 0;
}
cpp
复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
void sys_err(const char* str) {
perror(str);
exit(1);
}
void* tfn(void* arg) { //子线程
printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());
return NULL;
}
int main(int argc, char* argv[]) {
pthread_t tid;
//主线程
printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self()); //l-long u-unsigned
int ret = pthread_create(&tid, NULL, tfn, NULL);
if (ret != 0) {
perror("pthread_create error");
}
//sleep(1); //防止进程先结束,导致子线程没有地址空间
//return 0;
pthread_exit((void*) 0); //不退出总进程,退出主线程
}
六、pthread_join回收线程
cpp
复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
struct thrd {
int var;
char str[256];
};
void sys_err(const char* str) {
perror(str);
exit(1);
}
void* tfn(void* arg) {
struct thrd *tval; //必须用指针,不能是局部变量,因为在这个函数外就不存在了
tval = malloc(sizeof(tval));
tval->var = 100;
strcpy(tval->str, "hello thread");
return (void*)tval;
}
int main(int argc, char* argv[]) {
pthread_t tid;
struct thrd* retval;
int ret = pthread_create(&tid, NULL, tfn, NULL);
if (ret != 0) {
sys_err("pthread_create error");
}
// 回收线程,retval是传出参数,把tval内容拿到手
ret = pthread_join(tid, (void**)&retval);
if (ret != 0) {
sys_err("pthread_join error");
}
printf("child thread exit with var = %d, str = %s\n", retval->var, retval->str);
pthread_exit(NULL);
}
cpp
复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
void sys_err(const char* str) {
perror(str);
exit(1);
}
void* tfn(void* arg) {
return (void*)74;
}
int main(int argc, char* argv[]) {
pthread_t tid;
int* retval;
int ret = pthread_create(&tid, NULL, tfn, NULL);
if (ret != 0) {
sys_err("pthread_create error");
}
// 回收线程,retval是传出参数,把tval内容拿到手
ret = pthread_join(tid, (void**)&retval);
if (ret != 0) {
sys_err("pthread_join error");
}
printf("child thread exit with %d\n", (void*)retval);
pthread_exit(NULL);
}
cpp
复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
struct thrd {
int var;
char str[256];
};
void sys_err(const char* str) {
perror(str);
exit(1);
}
void* tfn(void* arg) {
struct thrd* tval = (struct thrd*)arg;
tval->var = 100;
strcpy(tval->str, "hello thread");
return (void*)tval;
}
int main(int argc, char* argv[]) {
pthread_t tid;
struct thrd arg;
struct thrd *retval;
int ret = pthread_create(&tid, NULL, tfn, (void*)&arg);
if (ret != 0) {
sys_err("pthread_create error");
}
ret = pthread_join(tid, (void**)&retval);
if (ret != 0) {
sys_err("pthread_join error");
}
printf("child thread exit with var = %d, str = %s\n", retval->var, retval->str);
pthread_exit(NULL);
}
七、pthread_cancel终止线程
cpp
复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
void* tfn(void* arg) { //子线程
while (1) {
printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());
sleep(1);
}
return NULL;
}
int main(int argc, char* argv[]) {
pthread_t tid;
int ret = pthread_create(&tid, NULL, tfn, NULL);
if (ret != 0) {
fprintf(stderr, "pthread_create error:%s\n", strerror(ret));
exit(1);
}
printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self());
sleep(5);
ret = pthread_cancel(tid);
if (ret != 0) {
fprintf(stderr, "pthread_cancel error:%s\n", strerror(ret));
exit(1);
}
while (1);
pthread_exit((void*)0); //不退出总进程,退出主线程
}
//5秒后子线程被杀死
cpp
复制代码
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
void* tfn1(void* arg) {
printf("thread 1 returning\n");
return (void*)111; // exit(111);
}
void* tfn2(void* arg) {
printf("thread 2 exiting\n");
pthread_exit((void*)222);
}
void* tfn3(void* arg) {
while (1) {
printf("thread 3: I'm going to die in 3 seconds ...\n");
sleep(1);
//pthread_testcancel(); // 自己添加取消点
}
return (void*)666;
}
int main(int argc, char* argv[]) {
pthread_t tid;
void* tret = NULL;
pthread_create(&tid, NULL, tfn1, NULL);
pthread_join(tid, &tret);
printf("thread 1 exit code = %d\n\n", (int)tret);
pthread_create(&tid, NULL, tfn2, NULL);
pthread_join(tid, &tret);
printf("thread 2 exit code = %d\n\n", (int)tret);
pthread_create(&tid, NULL, tfn3, NULL);
sleep(3);
pthread_cancel(tid);
pthread_join(tid, &tret);
printf("thread 3 exit code = %d\n\n", (int)tret);
return 0;
}
//-1表示非正常退出(被杀死)
//成功被pthread_cancel杀死的线程,返回-1 使用pthread_join回收。
cpp
复制代码
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
void* tfn1(void* arg) {
printf("thread 1 returning\n");
return (void*)111; // exit(111);
}
void* tfn2(void* arg) {
printf("thread 2 exiting\n");
pthread_exit((void*)222);
}
void* tfn3(void* arg) {
while (1) {
//printf("thread 3: I'm going to die in 3 seconds ...\n");
//sleep(1);
pthread_testcancel(); // 自己添加取消点
}
return (void*)666;
}
int main(int argc, char* argv[]) {
pthread_t tid;
void* tret = NULL;
pthread_create(&tid, NULL, tfn1, NULL);
pthread_join(tid, &tret);
printf("thread 1 exit code = %d\n\n", (int)tret);
pthread_create(&tid, NULL, tfn2, NULL);
pthread_join(tid, &tret);
printf("thread 2 exit code = %d\n\n", (int)tret);
pthread_create(&tid, NULL, tfn3, NULL);
sleep(3);
pthread_cancel(tid); // 需要进内核的契机
pthread_join(tid, &tret);
printf("thread 3 exit code = %d\n\n", (int)tret);
return 0;
}
//tfn3里面如果没有系统调用,就无法杀死线程
//if,for都无法进内核
八、pthread_detach线程分离
cpp
复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
void* tfn(void* arg) {
printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());
return NULL;
}
int main(int argc, int argv[]) {
pthread_t tid;
int ret = pthread_create(&tid, NULL, tfn, NULL);
if (ret != 0) {
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
exit(1);
}
ret = pthread_detach(tid); //设置线程分离线程终止,会自动清理pcb,无需回收
if (ret != 0) {
fprintf(stderr, "pthread_detach error: %s\n", strerror(ret));
exit(1);
}
sleep(1);
ret = pthread_join(tid, NULL);
if (ret != 0) {
fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
exit(1);
}
printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self());
pthread_exit((void*)0);
}
//线程分离之后结束,自己回收掉相关信息,导致pthread_join无法回收(tid),从而出错
九、线程属性设置分离线程
cpp
复制代码
线程属性:
设置分离属性。
pthread_attr_t attr; 创建一个线程属性结构体变量
pthread_attr_init(&attr); 初始化线程属性
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 设置线程属性为分离态
pthread_create(&tid, &attr, tfn, NULL); 借助修改后的设置线程属性创建为分离态的新线程
pthread_attr_destroy(&attr); 销毀线程属性
cpp
复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
void* tfn(void* arg) {
printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());
return NULL;
}
int main(int argc, int argv[]) {
pthread_t tid;
pthread_attr_t attr;
int ret = pthread_attr_init(&attr);
if (ret != 0) {
fprintf(stderr, "attr_init error: %s\n", strerror(ret));
exit(1);
}
ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 设置线程属性为分离属性
if (ret != 0) {
fprintf(stderr, "attr_setdetachstate error:%s\n", strerror(ret));
exit(1);
}
ret = pthread_create(&tid, &attr, tfn, NULL);
if (ret != 0) {
perror("pthread_create error");
}
ret = pthread_attr_destroy(&attr);
if (ret != 0) {
fprintf(stderr, "attr_destroy error:%s\n", strerror(ret));
exit(1);
}
//pthread_join是阻塞回收
ret = pthread_join(tid, NULL); // 验证是否分离
if (ret != 0) {
fprintf(stderr, "pthread_join error:%s\n", strerror(ret));
exit(1);
}
printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self());
pthread_exit((void*)0);
}
十、线程使用注意事项
cpp
复制代码
1.主线程退出其他线程不退出,主线程应调用pthread_exit。
2.避免僵尸线程。
pthreadjoin
pthread_detach
pthread_create指定分离属性。
被join的线程可能在join函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值
3.malloc和mmap申请的内存可以被其他线程释放(堆区共享)
4.应避免在多线程模型中调用 fork,子进程中只有调用fork的线程存在,其他线程在子进程
中均 pthread_exit,(进程中的一个线程fork,fork出来的进程中只有调用fork的线程存在)
5.信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制。
十一、线程同步
cpp
复制代码
线程同步,指一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。
同时其它线程为保证数据一致性,不能调用该功能。
线程同步:
协同步调,对公共区域数据按序访问。
放置数据混乱,产生与时间有关的错误。
数据混乱原因:
1.资源共享(独享资源则不会)
2.调度随机(意味着数据访问会出现竞争)
3.线程间缺乏必要的同步机制。
解决:使多个线程在访问共享资源的时候,出现互斥。
十二、mutex互斥锁
cpp
复制代码
Linux中提供一把互斥锁mutex(也称之为互斥量)。
每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。
锁本身不具备强制性。
资源还是共享的,线程间也还是竞争的。
但通过"锁"就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。
cpp
复制代码
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
pthread_mutex_t mutex; // 定义一个互斥锁
void* tfn(void* arg) {
srand(time(NULL));
while (1) {
phread_mutex_lock(&mutex); // 加锁
printf("hello ");
sleep(rand() % 3); // 模拟共享资源
printf("world\n");
pthread_mutex_unlock(&mutex); // 解锁
sleep(rand() % 3);
}
return NULL;
}
int main(void) {
pthread_t tid;
srand(time(NULL));
int ret = pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
if (ret != 0) {
fprintf(stderr, "mutex init error:%s\n", strerror(ret));
exit(1);
}
pthread_create(&tid, NULL, tfn, NULL);
while (1) {
pthread_mutex_lock(&mutex);
printf("HELLO ");
sleep(rand() % 3);
printf("WORLD\n");
phread_mutex_unlock(&mutex);
sleep(rand() % 3);
}
pthread_join(tid, NULL);
pthread_mutex_destroy(&mutex); // 销毁互斥锁
return 0;
}
// 用锁机制实现顺序打印大小写hello world
cpp
复制代码
如果sleep放在解锁之前就会出现以下情况:
因此访问完共享数据要立刻解锁
cpp
复制代码
注意事项:
尽量保证锁的粒度,越小越好。(访问共享数据前,加锁。访问结束立即解锁。)
互斥锁,初值为1。(pthread_mutex_init()函数调用成功。)
加锁:--操作(P操作),阻塞线程。
结束:++操作(V操作),唤醒阻塞的线程。
try锁:尝试加锁,成功--。失败则返回,设置错误号EBUSY
十三、读写锁
cpp
复制代码
读写锁特性:
1.读写锁是"写模式加锁"时,解锁前,所有对该锁加锁的线程都会被阻塞。
2.读写锁是"读模式加锁"时,如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。
3.读写锁是"读模式加锁"时,既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。
那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行(同时)阻塞,写锁优先级高
读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;
当它以写模式锁住时,它是以独占模式锁住的。
写独占、读共享。
读写锁非常适合于对线程读的次数远大于写的情况。
锁只有一把。以读方式给数据加锁一一读锁。以写方式给数据加锁一一写锁。
cpp
复制代码
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
int counter;
pthread_rwlock_t rwlock; // 全局的读写锁
// 3个线程不定时写同一全局资源,5个线程不定时读同一全局资源
void* th_write(void* arg) {
int t;
int i = (int)arg;
while (1) {
pthread_rwlock_wrlock(&rwlock);
t = counter;
usleep(1000);
printf("=======write %d: %lu: counter=%d ++counter=%d\n", i, pthread_self(), t, ++counter);
pthread_rwlock_unlock(&rwlock);
usleep(10000); // 给读锁让出CPU
}
return NULL;
}
void* th_read(void* arg){
int i = (int)arg;
while (1) {
pthread_rwlock_rdlock(&rwlock);
printf("------------read %d: %lu: %d\n", i, pthread_self(), counter);
pthread_rwlock_unlock(&rwlock);
usleep(2000);
}
return NULL;
}
int main(void) {
int i;
pthread_t tid[8];
pthread_rwlock_init(&rwlock, NULL);
for (i = 0; i < 3; i++) {
pthread_create(&tid[i], NULL, th_write, (void*)i);
}
for (i = 0; i < 5; i++) {
pthread_create(&tid[i + 3], NULL, th_read, (void*)i);
}
for (i = 0; i < 8; i++) {
pthread_join(tid[i], NULL);
}
pthread_rwlock_destroy(&rwlock);
return 0;
}
// 该程序印证了那读写锁逻辑
十四、条件变量实现生产者消费者模型
cpp
复制代码
条件变量:
本身不是锁!但是通常结合锁来使用。mutex
pthread_cond_t cond;
初始化条件变量:
1.pthread_cond_init(&cond, NULL); // 动态初始化。
2.pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 静态初始化
cpp
复制代码
/*借助条件变量模拟生产者-消费者问题*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
/*链表作为共享数据,需被互斥量保护*/
struct msg {
int num;
struct msg* next;
};
struct msg* head;
/*静态初始化一个条件变量和一个互斥量*/
pthread_cond_t has_data = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* consumer(void* arg) {
struct msg* mp;
while (1) {
pthread_mutex_lock(&mutex);
// 头指针为空,说明没有节点
if (head == NULL) {
// 阻塞等待条件变量,解锁,直到这个函数返回才重新加锁
pthread_cond_wait(&has_data, &mutex);
}
mp = head;
head = mp->next; // 模拟消费掉一个产品
pthread_mutex_unlock(&mutex); // 解锁互斥量
printf("------Consume:%d\n", mp->num);
free(mp);
sleep(rand() % 3);
}
return NULL;
}
void* producer(void* arg) {
struct msg* mp = malloc(sizeof(struct msg));
while(1) {
mp->num = rand() % 1000 + 1; // 模拟生产一个产品
printf("--Produce--- %d\n", mp->num);
pthread_mutex_lock(&mutex); // 加锁
mp->next = head;
head = mp;
pthread_mutex_unlock(&mutex); // 解锁
pthread_cond_signal(&has_data); // 将等待在该条件变量上的一个线程唤醒
sleep(rand() % 3);
}
return NULL;
}
int main(int argc, char* argv[]) {
int ret;
pthread_t pid, cid;
srand(time(NULL));
ret = pthread_create(&pid, NULL, producer, NULL); // 生产者
if (ret != 0) {
fprintf(stderr, "%s:%s\n", "pthread_create producer error", strerror(ret));
pthread_exit(NULL);
}
ret = pthread_create(&cid, NULL, consumer, NULL); // 消费者
if (ret != 0) {
fprintf(stderr, "%s:%s\n", "pthread_create consumer error", strerror(ret));
pthread_exit(NULL);
}
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}
十五、多个消费者一个生产者模型
cpp
复制代码
/*借助条件变量模拟生产者-消费者问题*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
/*链表作为共享数据,需被互斥量保护*/
struct msg {
int num;
struct msg* next;
};
struct msg* head;
/*静态初始化一个条件变量和一个互斥量*/
pthread_cond_t has_data = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* consumer(void* arg) {
struct msg* mp;
while (1) {
pthread_mutex_lock(&mutex);
// 头指针为空,说明没有节点
while (head == NULL) { // 使用循环重复判断临界区是否有数据
// 阻塞等待条件变量,解锁,直到这个函数返回才重新加锁
pthread_cond_wait(&has_data, &mutex);
}
mp = head;
head = mp->next; // 模拟消费掉一个产品
pthread_mutex_unlock(&mutex); // 解锁互斥量
printf("------Consume id: %lu :%d\n", pthread_self(), mp->num);
free(mp);
sleep(rand() % 3);
}
return NULL;
}
void* producer(void* arg) {
struct msg* mp = malloc(sizeof(struct msg));
while(1) {
mp->num = rand() % 1000 + 1; // 模拟生产一个产品
printf("--Produce--- %d\n", mp->num);
pthread_mutex_lock(&mutex); // 加锁
mp->next = head;
head = mp;
pthread_mutex_unlock(&mutex); // 解锁
pthread_cond_signal(&has_data); // 将等待在该条件变量上的一个线程唤醒
sleep(rand() % 3);
}
return NULL;
}
int main(int argc, char* argv[]) {
int ret;
pthread_t pid, cid;
srand(time(NULL));
ret = pthread_create(&pid, NULL, producer, NULL); // 生产者
if (ret != 0) {
fprintf(stderr, "%s:%s\n", "pthread_create producer error", strerror(ret));
pthread_exit(NULL);
}
ret = pthread_create(&cid, NULL, consumer, NULL); // 消费者
if (ret != 0) {
fprintf(stderr, "%s:%s\n", "pthread_create consumer error", strerror(ret));
pthread_exit(NULL);
}
ret = pthread_create(&cid, NULL, consumer, NULL); // 消费者
if (ret != 0) {
fprintf(stderr, "%s:%s\n", "pthread_create consumer error", strerror(ret));
pthread_exit(NULL);
}
ret = pthread_create(&cid, NULL, consumer, NULL); // 消费者
if (ret != 0) {
fprintf(stderr, "%s:%s\n", "pthread_create consumer error", strerror(ret));
pthread_exit(NULL);
}
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}
十六、用信号量实现生产者消费者模型
cpp
复制代码
/*信号量实现 生产者 消费者问题*/
//这才是真正的PV操作
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<stdio.h>
#include<semaphore.h>
#define NUM 5
int queue[NUM]; // 全局数组实现环形队列
sem_t blank_number, product_number; // 空格子信号量,产品信号量
void* producer(void* arg) {
int i = 0;
while (1) {
sem_wait(&blank_number); // 生产者将空格子数--,为0则阻塞等待
queue[i] = rand() % 1000 + 1; // 生产一个产品
printf("---Produce--%d\n", queue[i]);
sem_post(&product_number); // 将产品数++
i = (i + 1) % NUM; // 借助下标实现环形
sleep(rand() % 1);
}
}
void* consumer(void* arg)
{
int i = 0;
while (1) {
sem_wait(&product_number); // 消费者将产品数--,为0则阻塞等待
printf("-Consume---%d\n", queue[i]);
queue[i] = 0; // 消费一个产品
sem_post(&blank_number); // 消费掉以后,将空格子数++
i = (i + 1) % NUM;
sleep(rand() % 3);
}
}
int main(int argc, char* argv[]) {
pthread_t pid, cid;
sem_init(&blank_number, 0, NUM); // 初始化空格子信号量为5
sem_init(&product_number, 0, 0); // 产品数为0
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
sem_destroy(&blank_number);
sem_destroy(&product_number);
return 0;
}