pthread线程的装卸鸡蛋例子(deepseek给智商只有50的讲
(已略去thread代码及概念等对话)prompt: 给智商只有50的人注释上述代码. 对于所谓生产者之类的概念, 太抽象了,能不能用日常生活或者可感知的的电子产品应用来说明?deepseek: 好的我会用最生活化的语言把每一行代码都解释清楚。你不用担心听不懂的专业词我会把它们都变成厨房、冰箱、工人、本子这样的东西。#includestdio.h#includestdlib.h#includepthread.h#includesemaphore.h#includeunistd.h#includeerrno.h// ---------------------- 任务队列就像一个冰箱能放10个鸡蛋----------------------#defineQUEUE_SIZE10// 冰箱只能放10个鸡蛋typedefstruct{intdata[QUEUE_SIZE];// 放鸡蛋的格子inthead;// 下次从哪个格子拿鸡蛋inttail;// 下次把鸡蛋放哪个格子intcount;// 现在冰箱里有多少个鸡蛋pthread_mutex_tmutex;// 冰箱门的锁一次只让一个人开pthread_cond_tnot_full;// 条件冰箱没满可以放鸡蛋pthread_cond_tnot_empty;// 条件冰箱没空可以拿鸡蛋}task_queue_t;// 初始化冰箱把格子清空锁和条件准备好voidqueue_init(task_queue_t*q){q-headq-tailq-count0;pthread_mutex_init(q-mutex,NULL);pthread_cond_init(q-not_full,NULL);pthread_cond_init(q-not_empty,NULL);}// 销毁冰箱用完了要清理voidqueue_destroy(task_queue_t*q){pthread_mutex_destroy(q-mutex);pthread_cond_destroy(q-not_full);pthread_cond_destroy(q-not_empty);}// 往冰箱里放一个鸡蛋生产者用voidqueue_push(task_queue_t*q,intval){pthread_mutex_lock(q-mutex);// 拿起锁锁上门while(q-countQUEUE_SIZE){// 如果冰箱满了pthread_cond_wait(q-not_full,q-mutex);// 就站在冰箱前等直到有人拿走了鸡蛋有空位}q-data[q-tail]val;// 把鸡蛋放进格子q-tail(q-tail1)%QUEUE_SIZE;// 下次放的位置向后移一格q-count;// 鸡蛋数加1pthread_cond_signal(q-not_empty);// 喊一声冰箱不是空的了可以来拿鸡蛋了pthread_mutex_unlock(q-mutex);// 开锁让其他人可以用冰箱}// 从冰箱里拿一个鸡蛋消费者用intqueue_pop(task_queue_t*q,int*val,intshutdown_flag){pthread_mutex_lock(q-mutex);while(q-count0!shutdown_flag){// 如果冰箱是空的而且还没到收工时间pthread_cond_wait(q-not_empty,q-mutex);// 就站在冰箱前等直到有人放了鸡蛋}if(q-count0shutdown_flag){// 如果冰箱空了而且老板喊收工了pthread_mutex_unlock(q-mutex);return-1;// 就回去休息不再等了}*valq-data[q-head];// 拿出一个鸡蛋q-head(q-head1)%QUEUE_SIZE;// 下次拿的位置向后移一格q-count--;// 鸡蛋数减1pthread_cond_signal(q-not_full);// 喊一声冰箱有空位了可以继续放鸡蛋pthread_mutex_unlock(q-mutex);return0;// 成功拿到鸡蛋}// ---------------------- 全局统计信息用一把读写锁保护相当于一个公共账本----------------------typedefstruct{longtotal_processed;// 一共做了多少个鸡蛋菜处理了多少任务longtotal_errors;// 做菜搞砸了多少次pthread_rwlock_trwlock;// 账本的锁很多人可以同时看读但只有一个人能写改}stats_t;voidstats_init(stats_t*s){s-total_processed0;s-total_errors0;pthread_rwlock_init(s-rwlock,NULL);}voidstats_destroy(stats_t*s){pthread_rwlock_destroy(s-rwlock);}// 加一个菜成功做了一盘菜voidstats_add_processed(stats_t*s,intcount){pthread_rwlock_wrlock(s-rwlock);// 拿写锁独占账本s-total_processedcount;pthread_rwlock_unlock(s-rwlock);// 放开账本}// 记一次搞砸voidstats_add_error(stats_t*s,intcount){pthread_rwlock_wrlock(s-rwlock);s-total_errorscount;pthread_rwlock_unlock(s-rwlock);}// 查看账本只看不改voidstats_read(stats_t*s,long*processed,long*errors){pthread_rwlock_rdlock(s-rwlock);// 拿读锁可以很多人一起看*processeds-total_processed;*errorss-total_errors;pthread_rwlock_unlock(s-rwlock);}// ---------------------- 每个工人自己的小本本线程局部存储----------------------pthread_key_terror_key;// 小本本的钥匙pthread_once_tkey_oncePTHREAD_ONCE_INIT;// 保证只做一次小本本voidmake_error_key(){pthread_key_create(error_key,NULL);// 给小本本贴个标签}// 拿到自己的小本本每个工人一个不打架int*get_thread_error_ptr(){pthread_once(key_once,make_error_key);// 如果还没做小本本就做一个int*ptrpthread_getspecific(error_key);// 用自己的钥匙打开自己的小本本if(ptrNULL){ptrcalloc(1,sizeof(int));// 如果没有就新买一个本子pthread_setspecific(error_key,ptr);// 把本子挂到自己的钥匙上}returnptr;}// ---------------------- 全局的收工牌子老板说下班就下班----------------------staticvolatileintshutdown_flag0;// 0继续干1收工statictask_queue_ttask_queue;// 公共冰箱staticstats_tstats;// 公共账本staticsem_tsem_concurrency;// 信号量相当于厨房里的灶台数量一次只能3个人同时炒菜staticpthread_barrier_tstart_barrier;// 屏障大家集合好了一起开始干#defineNUM_WORKERS4// 4个工人#defineNUM_TASKS100// 一共要做100盘菜// ---------------------- 工人工作线程----------------------void*worker_thread(void*arg){longtid(long)arg;// 工人的编号1号工、2号工…intmy_errors0;// 这个工人自己搞砸的次数记在自己心里// 所有工人集合等人都到齐了再一起开始干活屏障pthread_barrier_wait(start_barrier);printf(Worker %ld started.\n,tid);while(1){// 1. 先抢一个灶台信号量P操作如果没有空灶台就等着sem_wait(sem_concurrency);// 2. 从冰箱拿一个鸡蛋任务inttask;if(queue_pop(task_queue,task,shutdown_flag)!0){sem_post(sem_concurrency);// 还回灶台break;// 冰箱空了而且老板让收工就下班}// 3. 炒菜处理任务耗时0.1秒printf(Worker %ld processing task %d\n,tid,task);usleep(100000);interror0;if(task%70){// 如果鸡蛋编号是7的倍数就会把菜炒糊error1;my_errors;printf(Worker %ld: task %d FAILED\n,tid,task);}else{printf(Worker %ld: task %d done\n,tid,task);}// 4. 更新公共账本成功数、失败数stats_add_processed(stats,1);if(error){stats_add_error(stats,1);}// 5. 用完灶台还回去信号量V操作sem_post(sem_concurrency);}// 把自己搞砸的次数记到小本本上线程局部存储int*global_error_ptrget_thread_error_ptr();*global_error_ptrmy_errors;printf(Worker %ld exiting, my errors %d\n,tid,my_errors);returnNULL;}// ---------------------- 老板生产者线程专门往冰箱里放鸡蛋----------------------void*producer_thread(void*arg){printf(Producer started.\n);for(inti0;iNUM_TASKS;i){queue_push(task_queue,i);// 放一个鸡蛋printf(Producer produced task %d\n,i);usleep(20000);// 放完一个休息0.02秒免得手太累}// 放完100个鸡蛋后告诉所有人我要关门了你们做完全部就下班吧shutdown_flag1;// 把还在等鸡蛋的工人叫醒别等了没鸡蛋了pthread_mutex_lock(task_queue.mutex);pthread_cond_broadcast(task_queue.not_empty);pthread_mutex_unlock(task_queue.mutex);printf(Producer finished.\n);returnNULL;}// ---------------------- 监工监控线程每隔1秒看看大家干得怎么样----------------------void*monitor_thread(void*arg){while(!shutdown_flag){sleep(1);longprocessed,errors;stats_read(stats,processed,errors);// 看一眼账本printf([MONITOR] Processed: %ld, Errors: %ld, Success rate: %.2f%%\n,processed,errors,(processed0?0.0:(1.0-errors/(double)processed)*100));}printf(Monitor exiting.\n);returnNULL;}// ---------------------- 大管家主函数----------------------intmain(){pthread_tworkers[NUM_WORKERS];pthread_tproducer,monitor;// 初始化冰箱、账本、灶台最多3个灶台、集合哨4个工人queue_init(task_queue);stats_init(stats);sem_init(sem_concurrency,0,3);pthread_barrier_init(start_barrier,NULL,NUM_WORKERS);// 招4个工人for(longi0;iNUM_WORKERS;i){pthread_create(workers[i],NULL,worker_thread,(void*)i);}// 招老板和监工pthread_create(producer,NULL,producer_thread,NULL);pthread_create(monitor,NULL,monitor_thread,NULL);// 等老板放完所有鸡蛋pthread_join(producer,NULL);// 等所有工人做完手头的菜for(inti0;iNUM_WORKERS;i){pthread_join(workers[i],NULL);}// 再等2秒让监工最后看一眼sleep(2);shutdown_flag1;pthread_join(monitor,NULL);// 打印最后的账本printf(\n Final Statistics \n);longtotal_processed,total_errors;stats_read(stats,total_processed,total_errors);printf(Total processed: %ld\n,total_processed);printf(Total errors: %ld\n,total_errors);printf(\nPer-thread error counts (from TLS):\n);// 这里只是告诉大家每个工人把自己的搞砸次数记在了自己的小本本上printf((TLS demonstration: each worker printed its own error count on exit)\n);// 打扫卫生销毁冰箱、账本、灶台、集合哨、小本本钥匙queue_destroy(task_queue);stats_destroy(stats);sem_destroy(sem_concurrency);pthread_barrier_destroy(start_barrier);pthread_key_delete(error_key);return0;}