我正在尝试完成一个使用多个线程(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, NULL, (void *(*)(void *))B, NULL );
pthread_create(&tid3, NULL, (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 exited\n");
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 exited\n");
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 exited\n");
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次-您可以将参数传递给线程函数以赋予它不同的操作。
printf()
语句而不是连续三个。return
。totalCalc()
函数折叠成一个printf()
调用后,该函数就没有太多优点了。PERCENTAGE
用词不当;它是分数,而不是百分比。我选择使用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, NULL, calculate, &a);
pthread_create(&tid2, NULL, calculate, &b);
pthread_create(&tid3, NULL, calculate, &c);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
printf("Total given out: %d\n", 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 = %.2f\n", data->name, result);
}
if (scholarship < 1)
{
printf("Thread %s exited\n", data->name);
pthread_mutex_unlock(&mutex);
return 0;
}
pthread_mutex_unlock(&mutex);
}
return 0;
}
和示例输出(在使用gcc 7.1.0的运行macOS Sierra 10.12.6的Mac上):
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 = %.2f\n", data->name, result);
pthread_mutex_unlock(&mutex);
}
printf("Thread %s exited\n", data->name);
return 0;
}
它仍然使用混合模式算术(浮点数和整数)。进一步的改进将涉及诸如修改main()
函数以使用数组而不是线程ID和控制结构的单独变量之类的事情。然后,您可以轻松拥有2-26个线程。您也可能会使用亚秒级睡眠。您可能有不同的线程对赠款剩余的资金慷慨解囊–您可以在不同线程中使用不同的分数,而不是固定的分数。
先前的两个版本中都存在一个问题(如user3629249在评论中指出的-尽管我已经有了代码的初步版本,其中包含了必要的修复程序;尚不支持)。calculate()
函数中的代码访问共享变量时scholarship
不持有互斥量。那真的不应该做。这是处理该问题的版本。它还会错误检查对pthread_*()
函数的调用,报告错误并在出现问题时退出。这很引人注目,但足以用于测试代码。的stderr.h
报头和配套源代码stderr.c
中可以找到https://github.com/jleffler/soq/tree/master/src/libsoq。错误处理在某种程度上掩盖了代码的操作,但是它与前面显示的非常相似。主要变化是互斥锁在进入循环之前被锁定,退出循环之后被解锁,在睡眠之前被解锁并且在唤醒之后被重新锁定。
该代码还使用随机分数(而不是一个固定分数)和随机的亚秒睡眠时间,并且它具有五个线程,而不是三个线程。它使用控制结构数组,并根据需要对其进行初始化。打印种子(当前时间)很不错;如果程序升级为处理命令行参数,它将允许您重现所使用的随机序列。(线程调度问题仍然存在不确定性。)
请注意,printf()
与原始调用中的三次调用相比,单个调用可改善输出的外观。原始代码可以(并且确实)交织来自不同线程的部分行。每个printf()
生产一条整条生产线,这不再是问题。您可以flockfile()
与它的朋友们一起看看发生了什么—规范中有一条概括的声明涵盖了其余的I / O库功能。
/* 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: %u\n", seed);
srand(seed);
int rc;
for (int i = 0; i < MAX_THREADS; i++)
{
ctrl[i].name[0] = 'A' + i;
ctrl[i].name[1] = '\0';
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], NULL, calculate, &ctrl[i])) != 0)
err_ptherror(rc, "Failed to create thread %d\n", i);
}
for (int i = 0; i < MAX_THREADS; i++)
{
if ((rc = pthread_join(tids[i], NULL)) != 0)
err_ptherror(rc, "Failed to join thread %d\n", i);
}
printf("Total given out: %d\n", total);
return 0;
}
static void *calculate(void *arg)
{
struct Data *data = arg;
printf("Thread %s: doze = 0.%03lds, fraction = %.3f\n",
data->name, 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 = %.2f\n", data->name, result);
}
if ((rc = pthread_mutex_unlock(&mutex)) != 0)
err_ptherror(rc, "Failed to unlock mutex (2) in %s()\n", __func__);
printf("Thread %s exited\n", 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
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句