下面是一个简单程序的 C 源代码。
该程序有一个主循环,要求用户在 3 个选项之间进行选择: 1-打印一些文本 2-做一些工作(没有副作用) 3-退出程序。
如果用户选择 2,则调用一个函数(称为恶作剧),该函数代表计算成本高昂、可并行的问题。此函数利用 pthreads 来利用多核 CPU 架构,但请注意,“shenanigans”函数确保“加入”每个创建的线程,并且在所有工作线程完成之前不会返回。
“恶作剧”函数的多线程性质应该对程序的其余部分完全透明。
这是源代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <pthread.h>
int convert_to_integer(const char *str, int *outValue) {
if (str == NULL || outValue == NULL) return -1; // NULL pointer error
char *endptr;
errno = 0;
long temp = strtol(str, &endptr, 10);
if (errno == ERANGE && (temp == LONG_MAX || temp == LONG_MIN))
return -2; // Overflow or underflow
if (errno != 0 && temp == 0)
return -3; // Other conversion error
if (endptr == str)
return -4; // No digits were found
if (*endptr != '\0')
return -5; // Additional characters after number
if (temp > INT_MAX || temp < INT_MIN)
return -6; // Value out of int range
*outValue = (int)temp;
return 0; // Success
}
int get_int_from_stdin(int *target) {
if (target == NULL) return -1; // NULL pointer error
char input[64];
if (fgets(input, sizeof(input), stdin) == NULL)
return -2; // Reading error or EOF
// null-terminate the string if newline is found
size_t len = strlen(input);
if (input[len - 1] == '\n') {
input[len - 1] = '\0';
} else {
// Clear stdin buffer if input exceeded buffer length, to avoid leftover input affecting subsequent reads
int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
if (len == sizeof(input) - 1) return -3; // Input too long
}
int value;
int result = convert_to_integer(input, &value);
if (result != 0) return result; // Propagate error code from conversion
*target = value;
return 0; // Success
}
void * thread_function(void * args) {
// this is work that will be done by threads
// notice there is no side effects and no interaction with
// stdout and stdin
// just waste a bunch of time
long x = 0;
for (int i = 0; i < 10000; i++) {
for (int j = 0; j < 1000000; j++) {
if (i %2 == 0) x++;
else x--;
}
}
return NULL;
}
void shenanigans(){
// run 7 different worker threads in parallel
printf("running tasks in parallel\n");
fflush(stdout); // flushing the output buffer doesn't do anything
//---- start of multithreading code
pthread_t threads[7];
// initiate 7 worker threads to do some work
for (int j = 0; j < 7; j++) pthread_create(&threads[j], NULL, thread_function, NULL);
// wait for all threads to finish
for (int j = 0; j < 7; j++) pthread_join(threads[j], NULL);
// ---- end of multithreading code
printf("task finished...\n");
}
void print_junk(){
printf("Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n");
}
int main() {
while (1) {
printf("\nChoose an option from the following menu: \n");
printf("1- do some calculations\n");
printf("2- print some junk to standard output\n");
printf("3- exit...\n");
int choice;
int result = get_int_from_stdin(&choice);
if (result != 0) {
printf("Invalid input, try again\n");
continue;
}
if (choice == 1) {
// this function uses parallelism to get its job done faster
// but the parallelism ought to be completely transparent to the main function
shenanigans();
} else if (choice == 2) {
print_junk();
} else if (choice == 3) {
break;
} else {
printf("Invalid choice, try again\n");
}
}
return 0;
}
当我编译并执行这段代码时,我遇到了一些奇怪的零星错误:
1-
在主循环中,当用户输入选项“1”时,我希望在主线程阻塞等待工作线程完成之前,“并行运行任务”行立即显示在控制台上。然而,这并非总是如此。很多时候,当我在控制台输入“1”后,程序会变得无响应,因为工作线程正在执行其工作,并且只有经过一段时间后,我才会看到“并行运行任务”打印在控制台上
2-出于某种奇怪的原因,似乎当主线程等待工作线程完成时,一些字符被推入输入缓冲区,当我尝试读取用户的选择时,会在主循环中造成不良的副作用.这是一张描述我所指问题的图片 这些竞争条件错误是最奇怪的,因为: 1-工作线程中的输入和输出为零。 2-线程之间存在零共享内存或全局状态突变
我在这里缺少什么?
stdout
和
stdin
共享锁一致,鉴于它们是同一终端,这并不奇怪。根据 C11 标准(草案)的
每个流都有一个关联的锁,用于防止多个执行线程访问流时出现数据争用,并限制多个线程执行的流操作的交错。一次只有一个线程可以持有该锁。锁是可重入的:单个线程可以在给定时间多次持有锁。您可以在
strace
下运行进程并检查输出以了解锁定使用情况。