多线程(C)程序线程无法终止
我正在尝试完成一个程序,该程序使用多个线程(3)来分发4000美元的假设奖学金.每次线程处理时,它“锁定”“临界区”并阻止其他线程从总和中取出它们的块.每次访问时,该帖子都要占用“奖学金”余额的25%.输出是每个线程获得奖学金访问权所需的金额.
到目前为止,我的程序似乎正在处理正确的输出,但是当它到达结束时似乎有一个问题.每个进程/线程都到达一个不终止或退出的程序,程序就会停滞不前并且无法完成.我觉得线程正在处理,但没有达到终止条件(奖学金全部消失).永远不会达到最后一个函数totalCalc().有没有人看到我没有的东西,这有助于缓解这个问题或推动程序完成? #include <stdio.h> #include <pthread.h> #include <math.h> #define PERCENTAGE 0.25 pthread_mutex_t mutex; // protecting critical section int scholarship = 4000,total = 0; void *A(); void *B(); void *C(); void *totalCalc(); int main(){ pthread_t tid1,tid2,tid3; //pthread_setconcurrency(3); pthread_create(&tid1,NULL,(void *(*)(void *))A,NULL ); pthread_create(&tid2,(void *(*)(void *))B,NULL ); pthread_create(&tid3,(void *(*)(void *))C,NULL ); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); totalCalc(); return 0; } void *A(){ float result; while(scholarship > 0){ sleep(2); pthread_mutex_lock(&mutex); result = scholarship * PERCENTAGE; result = ceil(result); total = total + result; scholarship = scholarship - result; if( result >= 1){ printf("A = "); printf("%.2f",result); printf("n"); } if( scholarship < 1){ pthread_exit(0); printf("Thread A exitedn"); return; } pthread_mutex_unlock(&mutex); } pthread_exit(0); } void *B(){ float result; while(scholarship > 0){ sleep(1); pthread_mutex_lock(&mutex); result = scholarship * PERCENTAGE; result = ceil(result); total = total + result; scholarship = scholarship - result; if( result >= 1){ printf("B = "); printf("%.2f",result); printf("n"); } if( scholarship < 1){ pthread_exit(0); printf("Thread B exitedn"); return; } pthread_mutex_unlock(&mutex); } pthread_exit(0); } void *C(){ float result; while(scholarship > 0){ sleep(1); pthread_mutex_lock(&mutex); result = scholarship * PERCENTAGE; result = ceil(result); total = total + result; scholarship = scholarship - result; if( result >= 1){ printf("C = "); printf("%.2f",result); printf("n"); } if( scholarship < 1){ pthread_exit(0); printf("Thread C exitedn"); return; } pthread_mutex_unlock(&mutex); } pthread_exit(0); } void *totalCalc(){ printf("Total given out: "); printf("%d",total); printf("n"); } 输出: B = 1000.00 C = 750.00 A = 563.00 B = 422.00 C = 317.00 B = 237.00 C = 178.00 A = 134.00 B = 100.00 C = 75.00 B = 56.00 C = 42.00 A = 32.00 B = 24.00 C = 18.00 B = 13.00 C = 10.00 A = 8.00 B = 6.00 C = 4.00 B = 3.00 C = 2.00 A = 2.00 B = 1.00 C = 1.00 B = 1.00 C = 1.00 ^C 解决方法
你不应该将相同的函数写出3次 – 你可以将一个参数传递给线程函数来给它做不同的事情.
>您应该初始化您的互斥锁. 我选择使用return而不是调用pthread_exit();差异并不重要. 第一组清理 这是您的代码的修订版. #include <math.h> #include <pthread.h> #include <stdio.h> #include <unistd.h> #define FRACTION 0.25 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static int scholarship = 4000; static int total = 0; static void *calculate(void *data); struct Data { const char *name; int doze; }; int main(void) { pthread_t tid1; pthread_t tid2; pthread_t tid3; struct Data a = { "A",2 }; struct Data b = { "B",1 }; struct Data c = { "C",1 }; pthread_create(&tid1,calculate,&a); pthread_create(&tid2,&b); pthread_create(&tid3,&c); pthread_join(tid1,NULL); printf("Total given out: %dn",total); return 0; } static void *calculate(void *arg) { struct Data *data = arg; float result; while (scholarship > 0) { sleep(data->doze); pthread_mutex_lock(&mutex); result = scholarship * FRACTION; result = ceil(result); total = total + result; scholarship = scholarship - result; if (result >= 1) { printf("%s = %.2fn",data->name,result); } if (scholarship < 1) { printf("Thread %s exitedn",data->name); pthread_mutex_unlock(&mutex); return 0; } pthread_mutex_unlock(&mutex); } return 0; } 和示例输出(在Mac上运行macOS Sierra 10.12.6,使用GCC 7.1.0): B = 1000.00 C = 750.00 A = 563.00 B = 422.00 C = 317.00 B = 237.00 C = 178.00 A = 134.00 B = 100.00 C = 75.00 B = 56.00 C = 42.00 A = 32.00 C = 24.00 B = 18.00 C = 13.00 B = 10.00 A = 8.00 C = 6.00 B = 4.00 B = 3.00 C = 2.00 A = 2.00 B = 1.00 C = 1.00 B = 1.00 C = 1.00 Thread C exited Thread A exited Thread B exited Total given out: 4000 第一阶段改进 请记住:通常可以改进工作代码.这是calculate()函数的另一个修订版,它可以更清晰地处理终止条件. static void *calculate(void *arg) { struct Data *data = arg; while (scholarship > 0) { sleep(data->doze); pthread_mutex_lock(&mutex); float result = ceil(scholarship * FRACTION); total += result; scholarship -= result; if (result >= 1) printf("%s = %.2fn",result); pthread_mutex_unlock(&mutex); } printf("Thread %s exitedn",data->name); return 0; } 它仍然使用混合模式算法(浮点和整数).进一步的改进将涉及修改main()函数以使用数组而不是线程ID和控制结构的单独变量.然后你可以很容易地拥有2-26个线程.您也可以使用亚秒级睡眠.您可能有不同的线程与赠款的剩余部分不同 – 而不是固定的部分,您可以在不同的线程中使用不同的分数. 全唱,全舞的版本 在以前的版本中都有一个问题(正如user3629249在comment中所指出的那样 – 尽管我已经有了一个初步版本的代码,其中包含了必要的修复;它还没有在SO上). calculate()函数中的代码访问共享变量奖学金而不持有互斥量.这不应该真的做到.这是一个处理它的版本.它还会错误地检查对pthread _ *()函数的调用,报告错误并在出现问题时退出.这是戏剧性但足以用于测试代码.可以在https://github.com/jleffler/soq/tree/master/src/libsoq中找到stderr.h头和支持源代码stderr.c.错误处理在某种程度上掩盖了代码的操作,但它与之前显示的非常相似.主要变化是互斥锁在进入循环之前被锁定,在退出循环之后解锁,在睡眠之前解锁并在唤醒之后重新锁定. 此代码还使用随机分数而不是一个固定分数,以及随机亚秒级睡眠时间,它有五个线程而不是三个线程.它使用控制结构数组,根据需要对其进行初始化.打印种子(当前时间)是一个非常好的;它将允许您重现在程序升级到处理命令行参数时使用的随机序列. (线程调度问题仍然存在不确定性.) 请注意,与原始三重调用相比,对printf()的单次调用可改善输出的外观.原始代码可以(并且确实)交错来自不同线程的部分行.每个printf()产生一条整行,这不再是一个问题.您可以查看 /* SO 4544-8840 Multithreaded C program - threads not terminating */ #include "stderr.h" // https://github.com/jleffler/soq/tree/master/src/libsoq #include <errno.h> #include <math.h> #include <pthread.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static int scholarship = 4000; static int total = 0; static void *calculate(void *data); enum { MAX_THREADS = 5 }; enum { MIN_PERCENT = 10,MAX_PERCENT = 25 }; struct Data { char name[2]; struct timespec doze; double fraction; }; static inline double random_fraction(void) { return (double)rand() / RAND_MAX; } static inline _Noreturn void err_ptherror(int rc,const char *fmt,...) { errno = rc; va_list args; va_start(args,fmt); err_print(ERR_SYSERR,ERR_STAT,fmt,args); va_end(args); exit(EXIT_FAILURE); } int main(int argc,char **argv) { err_setarg0(argv[argc-argc]); pthread_t tids[MAX_THREADS]; struct Data ctrl[MAX_THREADS]; unsigned seed = time(0); printf("Seed: %un",seed); srand(seed); int rc; for (int i = 0; i < MAX_THREADS; i++) { ctrl[i].name[0] = 'A' + i; ctrl[i].name[1] = ' '; ctrl[i].doze.tv_sec = 0; ctrl[i].doze.tv_nsec = 100000000 + 250000000 * random_fraction(); ctrl[i].fraction = (MIN_PERCENT + (MAX_PERCENT - MIN_PERCENT) * random_fraction()) / 100; if ((rc = pthread_create(&tids[i],&ctrl[i])) != 0) err_ptherror(rc,"Failed to create thread %dn",i); } for (int i = 0; i < MAX_THREADS; i++) { if ((rc = pthread_join(tids[i],NULL)) != 0) err_ptherror(rc,"Failed to join thread %dn",i); } printf("Total given out: %dn",total); return 0; } static void *calculate(void *arg) { struct Data *data = arg; printf("Thread %s: doze = 0.%03lds,fraction = %.3fn",data->doze.tv_nsec / 1000000,data->fraction); int rc; if ((rc = pthread_mutex_lock(&mutex)) != 0) err_ptherror(rc,"Failed to lock mutex (1) in %s()n",__func__); while (scholarship > 0) { if ((rc = pthread_mutex_unlock(&mutex)) != 0) err_ptherror(rc,"Failed to unlock mutex (1) in %s()n",__func__); nanosleep(&data->doze,NULL); if ((rc = pthread_mutex_lock(&mutex)) != 0) err_ptherror(rc,"Failed to lock mutex (2) in %s()n",__func__); double result = ceil(scholarship * data->fraction); total += result; scholarship -= result; if (result >= 1) printf("%s = %.2fn",result); } if ((rc = pthread_mutex_unlock(&mutex)) != 0) err_ptherror(rc,"Failed to unlock mutex (2) in %s()n",__func__); printf("Thread %s exitedn",data->name); return 0; } 您仍然可以对代码进行修改,以便在睡眠后检查奖学金金额,从而在循环体中打破无限循环.这些变化留给读者作为一个小练习. 示例运行 Seed: 1501727930 Thread A: doze = 0.119s,fraction = 0.146 Thread B: doze = 0.199s,fraction = 0.131 Thread C: doze = 0.252s,fraction = 0.196 Thread D: doze = 0.131s,fraction = 0.102 Thread E: doze = 0.198s,fraction = 0.221 A = 584.00 D = 349.00 E = 678.00 B = 314.00 A = 303.00 C = 348.00 D = 146.00 A = 187.00 D = 112.00 E = 217.00 B = 100.00 A = 97.00 C = 111.00 D = 47.00 E = 90.00 A = 47.00 B = 36.00 D = 24.00 A = 31.00 C = 36.00 D = 15.00 E = 29.00 B = 13.00 A = 13.00 D = 8.00 A = 10.00 E = 13.00 B = 6.00 C = 8.00 D = 3.00 A = 4.00 D = 3.00 E = 4.00 B = 2.00 A = 2.00 C = 2.00 D = 1.00 A = 2.00 E = 2.00 B = 1.00 A = 1.00 D = 1.00 Thread D exited Thread C exited Thread A exited Thread E exited Thread B exited Total given out: 4000 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |