使用Pthread的多线程程序中,堆栈是如何工作的?

3

我有一个简单的问题,就是关于多线程程序的。据我所知,多线程程序共享进程的内存空间,包括栈、全局内存区域、文件描述符等。我想知道为什么在第一个示例中存在一致性问题,因为理论上所有线程都共享栈,在第二个示例中会出现竞争问题。

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

void *thr(void *arg)
{
   for(int i = 0; i < 50; i++)
       printf("Thread = %zu Value= %d\n", pthread_self(), i);
   return NULL;
}

int main(void)
{
   pthread_t threads[2];
   for(int i = 0; i < 2; i++)
     pthread_create(&threads[i], NULL, thr, NULL);
   for(int i = 0; i < 2; i++)
     pthread_join(threads[i], NULL);
   return 0;
}

第二个运行问题的程序


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

int i = 0;

void *thr(void *arg)
{
   for(; i < 50; i++)
     printf("Thread = %zu Value= %d\n", pthread_self(), i);
   return NULL;
}

int main(void)
{
   pthread_t threads[2];
   for(int i = 0; i < 2; i++)
     pthread_create(&threads[i], NULL, thr, NULL);
   for(int i = 0; i < 2; i++)
     pthread_join(threads[i], NULL);
   return 0;
}

以下是与竞态问题相关的3个示例,这些变量在主线程中创建,并作为参数传递给函数,也就是说,唯一共享的堆栈来自于主线程?

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

void *thr(void *arg)
{
   int *ptr = (int *)arg;
   for(; *ptr < 50; (*ptr)++)
     printf("Thread = %zu Value= %d\n", pthread_self(), *ptr);
   return NULL;
}

int main(void)
{
   int i = 0;
   pthread_t threads[2];
   for(int i = 0; i < 2; i++)
     pthread_create(&threads[i], NULL, thr, &i);
   for(int i = 0; i < 2; i++)
     pthread_join(threads[i], NULL);
   return 0;
}
2个回答

7
分享进程的内存空间是在所有线程之间共享,包括栈。
是和不是。
共享地址空间和共享特定的内存区域有所不同。
虽然所有线程共享地址空间,但每个线程都有自己的堆栈(内存分配)。
共享地址空间意味着给定的虚拟地址(指针值)在所有线程中都指向相同的物理内存。
但是专用的堆栈意味着每个线程的堆栈指针从该地址空间的不同位置开始,以避免彼此冲突。

主线程堆栈是共享的吗?因为当我将其作为参数传递时,其他线程可以访问它,但是当我在另一个线程上创建一个堆栈变量时,它不是共享的,这似乎就是问题所在。 - Yuri Bittencourt
1
堆栈都存在相同的地址空间中,但该空间内的区域是独立的。 - Jonathon Reinhart
1
@YuriBittencourt:所有线程都可以访问所有其他线程的堆栈,因为它们在同一个虚拟地址空间中。例如,如果一个线程被传递到另一个线程堆栈上的变量指针,它可以无任何限制地访问该变量(尽管可能会因CPU缓存的工作方式而产生性能损失)。 - Andreas Wenzel
为什么在第一个例子中,变量i似乎对其他线程是可见的,而在第三个例子中,变量i对其他线程是可见的? - Yuri Bittencourt
@AndreasWenzel? - Yuri Bittencourt
显示剩余11条评论

1

进程中的每个线程通常都有自己的堆栈指针(意味着它们有自己的堆栈)。

这意味着虽然它们共享虚拟地址空间,可以访问其他线程的变量或甚至堆栈,但它们通常在不同的、不重叠的位置上拥有自己的堆栈,位于这个共享的虚拟内存空间中。

当内核调度一个线程时,它会将线程的寄存器安装到它要使用的核心或 CPU 中(包括每个线程的堆栈值为 stack 的堆栈指针)。事实上,内核为每个线程维护一个用户模式堆栈和一个内核堆栈(后者允许同一进程的两个不同线程同时执行系统调用——相同或不同——如果它们必须共享单个堆栈,则这是不可能的)。


非常欢迎您!:) - Luis Colorado

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接