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

C语言双线程用Mutex和条件变量打印序列异常:仅输出B序列

线程同步问题:mutex+条件变量打印指定模式失败分析

问题描述

需要用两个线程结合互斥锁(mutex)和条件变量打印序列A0B1A2B3A4B5A6B7A8B9,模式存储在字符数组中,用全局变量string_index记录当前打印位置。预期逻辑:

  • 先创建线程A,它先锁定mutex并执行打印,随后休眠等待信号
  • 线程B拿到mutex后打印下一个序列,发送信号唤醒A,循环往复

但实际运行后只输出B0B1B2B3B4B5B6B7B8B9,线程A完全没执行。

原代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

int string_index = 0;
char string_to_print[10] = "0123456789";
pthread_mutex_t lock;
pthread_cond_t cond;

void *funcA(void *arg)
{
    pthread_mutex_lock(&lock); // Lock mutex

    while (1)
    {
        printf("A%c", string_to_print[string_index]);
        string_index++;

        // Sleep and wait for signal
        pthread_cond_wait(&cond, &lock);
    }
}

void *funcB(void *arg)
{
    while (1)
    {
        pthread_mutex_lock(&lock); // Lock mutex
        printf("B%c", string_to_print[string_index]);
        string_index++;

        if(string_index > 9)
        {
            string_index = 0;
            printf("\n");
        }

        // Unlock mutex
        pthread_mutex_unlock(&lock);

        // Send cond var signal
        pthread_cond_signal(&cond);
    }
}

int main(void)
{
    // Initialize mutex lock
    if (pthread_mutex_init(&lock, NULL) != 0)
    {
        printf("\n mutex init has failed\n");
        return 1;
    }

    // Initialize conditional variable
    if (pthread_cond_init(&cond, NULL) != 0)
    {
        printf("cond var init failed");
        return 1;
    }

    pthread_t A, B;

    // Create thread A
    if (pthread_create(&A, NULL, &funcA, NULL) != 0)
    {
        printf("Error creating thread");
        exit(-1);
    }

    // Create thread B
    if (pthread_create(&B, NULL, &funcB, NULL) != 0)
    {
        printf("Error creating thread");
        exit(-1);
    }

    sleep(20);   /* Sleep 20 seconds to allow time
                    for the threads to run before
                    terminating them with
                    pthread_cancel()                */

    pthread_cancel(A);
    pthread_cancel(B);
    pthread_exit(NULL);
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);
}

错误原因分析

  1. 线程执行顺序的误解:
    线程创建后的执行顺序由操作系统调度器决定,并非先创建的线程一定会先运行。原代码假设线程A会先锁定mutex,但实际可能线程B先抢占到mutex,直接开始循环打印,而线程A一直在等待mutex解锁,根本没机会执行第一次打印。

  2. 条件变量使用不规范:
    pthread_cond_wait()必须配合条件判断使用,原代码中没有判断当前是否轮到该线程执行,即使线程被唤醒,也无法保证执行顺序。此外,缺少条件判断也无法处理操作系统的「虚假唤醒」问题(线程可能被无理由唤醒)。

  3. 线程A的逻辑缺陷:
    线程A在第一次打印后就进入pthread_cond_wait(),但线程B在打印后解锁mutex并发送信号,线程A被唤醒后会直接再次打印——但此时string_index已经被线程B递增过,导致打印内容错误;更糟的是,线程B的循环会反复尝试锁定mutex,可能在A唤醒后再次抢占锁,继续打印。

修正方案及代码

核心是添加一个「轮次标记」变量,明确当前哪个线程应该执行,配合条件变量实现严格的交替执行:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

int string_index = 0;
char string_to_print[10] = "0123456789";
pthread_mutex_t lock;
pthread_cond_t cond;
int turn = 0; // 0: 线程A执行,1: 线程B执行

void *funcA(void *arg)
{
    while (1)
    {
        pthread_mutex_lock(&lock);
        // 等待轮到自己执行,处理虚假唤醒
        while (turn != 0) {
            pthread_cond_wait(&cond, &lock);
        }
        // 打印当前内容并更新索引
        printf("A%c", string_to_print[string_index]);
        string_index++;
        // 切换轮次为线程B
        turn = 1;
        // 唤醒线程B
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);

        // 重置索引并换行,准备下一轮
        if (string_index >= 10) {
            string_index = 0;
            printf("\n");
            // 重置轮次为A,确保下一轮从A开始
            pthread_mutex_lock(&lock);
            turn = 0;
            pthread_mutex_unlock(&lock);
            sleep(1); // 延迟方便观察每一轮输出
        }
    }
}

void *funcB(void *arg)
{
    while (1)
    {
        pthread_mutex_lock(&lock);
        // 等待轮到自己执行,处理虚假唤醒
        while (turn != 1) {
            pthread_cond_wait(&cond, &lock);
        }
        // 打印当前内容并更新索引
        printf("B%c", string_to_print[string_index]);
        string_index++;
        // 切换轮次为线程A
        turn = 0;
        // 唤醒线程A
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
    }
}

int main(void)
{
    if (pthread_mutex_init(&lock, NULL) != 0)
    {
        printf("\n mutex init has failed\n");
        return 1;
    }

    if (pthread_cond_init(&cond, NULL) != 0)
    {
        printf("cond var init failed");
        return 1;
    }

    pthread_t A, B;

    if (pthread_create(&A, NULL, &funcA, NULL) != 0)
    {
        printf("Error creating thread");
        exit(-1);
    }

    if (pthread_create(&B, NULL, &funcB, NULL) != 0)
    {
        printf("Error creating thread");
        exit(-1);
    }

    sleep(5); // 缩短等待时间,避免过长阻塞

    pthread_cancel(A);
    pthread_cancel(B);
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);
    return 0;
}

关键修改点

  • 添加turn变量明确轮次,确保线程只在自己的回合执行打印
  • 用while循环包裹pthread_cond_wait(),重新检查条件以处理虚假唤醒
  • 每个线程打印后切换轮次,唤醒对方线程再释放mutex,保证执行顺序严格交替
  • 索引重置后同步轮次,确保下一轮从线程A开始

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 07:32:10