You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

含pthreads的简单C程序出现偶发Bug,求分析环境相关原因

虚拟机环境下多线程C程序的偶发异常原因

程序说明

以下是一个简单的C程序源码,主循环提供三个选项供用户选择:

    1. 执行计算任务
    1. 打印指定文本
    1. 退出程序

选择选项1时会调用shenanigans函数,该函数是计算密集型任务,通过pthreads实现多核并行。注意shenanigans会调用pthread_join等待所有工作线程完成后才返回,其多线程实现对程序其他部分完全透明。

源码

#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后,预期"running tasks in parallel"会立即输出,之后主线程才会等待工作线程完成,但实际常出现程序先无响应(工作线程运行中),一段时间后才显示该文本;
  • 主线程等待工作线程期间,输入缓冲区似乎被写入了无关字符,导致后续主循环读取用户选择时出现异常。

这些异常非常反常,因为:

  • 工作线程完全没有IO操作;
  • 线程间没有共享内存或全局状态修改。

排查后发现是环境问题:在Oracle VirtualBox托管的Debian虚拟机中会出现,换成VMware Workstation托管的Debian环境后问题消失。请问这背后的原因是什么?

原因分析

这两个异常本质是VirtualBox与VMware在虚拟IO/控制台处理和CPU调度模拟上的实现差异或缺陷导致的:

1. 输出延迟问题

虽然代码显式调用了fflush(stdout),但stdout的刷新还受终端行缓冲机制、虚拟机控制台转发逻辑的影响。VirtualBox在处理宿主机与虚拟机的IO转发时,可能存在缓冲同步延迟:主线程执行printf和fflush后,虚拟机内核的输出缓冲区数据并没有立即被转发到宿主机终端,反而被后续计算密集型线程抢占了CPU资源,负责控制台输出转发的内核线程长时间得不到调度,直到工作线程完成后才批量输出内容。

而VMware的控制台IO转发机制更高效,能及时调度IO相关线程,确保输出内容立即显示。

2. 输入缓冲区异常问题

这个问题大概率是VirtualBox的虚拟键盘/输入模拟bug:当虚拟机内CPU被计算密集型线程占满时,VirtualBox的输入模拟模块可能出现异常,将宿主机的键盘输入(甚至内部模拟信号)错误写入虚拟机的stdin缓冲区。这种情况属于虚拟机底层bug,和程序本身的线程逻辑无关——毕竟程序的工作线程完全没有触碰stdin资源。

VMware的输入模拟实现更稳定,不存在这类高负载场景下的异常写入问题。

另外补充:VirtualBox在多核虚拟机的CPU调度模拟上,对于高负载线程场景可能存在调度不公平性,导致IO处理内核线程被长时间抢占,进一步放大了上述两个问题。


内容的提问来源于stack exchange,提问作者ForeverStudent

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 08:35:54