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

pthreads共享数据访问问题:代码输出分析与同步方案

Pthreads多线程练习题答疑

我正在学习系统编程与操作系统,遇到某机构过往面试中的pthreads练习题,经调研仍有困惑,现提出以下技术问题:

  1. 如下代码的输出可能有多少种取值?
  2. 这些取值具体是什么?
  3. 产生不同取值的原因是什么?
  4. 如何避免出现不同取值?

附代码:

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

int shared_data = 100;

void* thread_function(void *args)
{
    long temp;
    long *j = (long *) args;
    temp = shared_data;
    temp = temp + (long)&j; // 取线程局部变量j的地址,转为long类型后累加
    shared_data = temp;
    
    return (NULL);
}

int main() 
{
    long i = 0;
    long tinfo[] = {1,2,3};
    pthread_t ids[3];

    for(int i=0 ;i<3;i++)
    {
        pthread_create(ids+i,NULL,&thread_function, &tinfo[i]);
    }

    for(int i=0 ;i<3;i++)
    {
        pthread_join(ids[i],NULL);
    }

    printf("shared data = %d\n",shared_data);
}

问题1:输出可能有多少种取值?

一共有7种不同的可能取值。

问题2:这些取值具体是什么?

三个线程的局部变量j位于各自独立的栈空间,所以它们的地址X1、X2、X3是三个不同的数值。可能的取值包括:

  • 仅单个线程的增量生效:100+X1、100+X2、100+X3(另外两个线程的写入被最后执行的线程覆盖)
  • 两个线程的增量生效:100+X1+X2、100+X1+X3、100+X2+X3(一个线程读取初始值并修改,另一个读取修改后的值再修改,第三个读取初始值并覆盖前两者的部分结果)
  • 三个线程的增量全部生效:100+X1+X2+X3(三个线程依次串行执行,每个都基于前一个的修改结果更新)

问题3:产生不同取值的原因是什么?

根本原因是竞态条件(Race Condition):

  • 对共享变量shared_data的更新不是原子操作,每个线程的逻辑拆成了三步:读取shared_data到局部变量、修改局部变量、写回shared_data。
  • 操作系统的线程调度是抢占式的,线程随时可能被暂停,其他线程会趁机修改shared_data。被暂停的线程恢复后,会基于之前读取的旧值修改并写回,直接覆盖其他线程的更新,最终结果完全取决于线程的执行顺序。

问题4:如何避免出现不同取值?

要消除竞态条件,必须保证同一时间只有一个线程能修改共享变量,或者让更新操作变成原子性的,常用的解决办法有两种:

方法1:使用互斥锁

通过互斥锁锁住共享变量的访问逻辑,确保同一时间只有一个线程执行修改操作:

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

int shared_data = 100;
pthread_mutex_t mutex; // 定义互斥锁

void* thread_function(void *args)
{
    long temp;
    long *j = (long *) args;
    pthread_mutex_lock(&mutex); // 进入临界区前加锁
    temp = shared_data;
    temp = temp + (long)&j;
    shared_data = temp;
    pthread_mutex_unlock(&mutex); // 退出临界区后解锁
    
    return (NULL);
}

int main() 
{
    pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
    long tinfo[] = {1,2,3};
    pthread_t ids[3];

    for(int i=0 ;i<3;i++)
    {
        pthread_create(ids+i,NULL,&thread_function, &tinfo[i]);
    }

    for(int i=0 ;i<3;i++)
    {
        pthread_join(ids[i],NULL);
    }

    printf("shared data = %d\n",shared_data);
    pthread_mutex_destroy(&mutex); // 销毁互斥锁
}

方法2:使用原子操作

如果编译器支持(比如GCC),可以用内置的原子操作函数直接完成累加,省去锁的开销:

void* thread_function(void *args)
{
    long *j = (long *) args;
    // 原子地将shared_data加上(long)&j,直接返回更新后的值
    __sync_add_and_fetch(&shared_data, (long)&j);
    
    return (NULL);
}

这两种方法都能保证三个线程的增量全部生效,最终输出固定为100+X1+X2+X3。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:01:01