涉及 pthreads 的最琐碎的 C 程序中出现奇怪的零星错误

问题描述 投票:0回答:1

下面是一个简单程序的 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-出于某种奇怪的原因,似乎当主线程等待工作线程完成时,一些字符被推入输入缓冲区,当我尝试读取用户的选择时,会在主循环中造成不良的副作用.

这是一张描述我所指问题的图片

enter image description here

这些竞争条件错误是最奇怪的,因为: 1-工作线程中的输入和输出为零。 2-线程之间存在零共享内存或全局状态突变

我在这里缺少什么?

c linux pthreads posix
1个回答
0
投票
您的输出与

stdout

stdin
 共享锁一致,鉴于它们是同一终端,这并不奇怪。

根据 C11 标准(草案)的

7.21.2 Streams,第 7 段:

每个流都有一个关联的锁,用于防止多个执行线程访问流时出现数据争用,并限制多个线程执行的流操作的交错。一次只有一个线程可以持有该锁。锁是可重入的:单个线程可以在给定时间多次持有锁。

您可以在

strace

 下运行进程并检查输出以了解锁定使用情况。

© www.soinside.com 2019 - 2024. All rights reserved.