2

目前,我试图在集群系统中实现共享堆栈。该系统有两个地址空间,一个是私有的,一个在所有处理器之间共享。

如何在 c 编程中更改堆栈的开始和结束?

即,我想将堆栈放在共享空间中并更改程序流程以使用它。

4

3 回答 3

1

我的建议是:不惜一切代价避免将指针传递给基于堆栈的对象。如果你这样做了,你将要求发送线程永远不会从创建共享对象的函数返回,然后才能证明没有其他线程将再以任何方式访问该对象。这 a) 几乎是不可能的,并且 b) 需要性能扼杀锁定。未能正确执行此操作的后果将是完全不确定的错误!不要这样做!

我建议 malloc所有要离开一个线程的上下文并对其使用线程安全引用计数的对象。您还可以查看线程安全的共享数据结构,如队列、双链表等。

于 2013-06-20T19:32:11.057 回答
1

我怀疑您能否在 C 中很好地实现您想要的(除非您想更改编译器)。

你说你想要一种 OpenMP 风格的编程。这相当于一个单一的顶级线程,它可以分叉子级以在父级提供的共享空间上进行操作。Fancy OpenMP 以递归方式执行此操作。

为了很好地做到这一点,您的系统/工具/编译器需要能够识别

*   threads of computations  
*   what variables are declared by each thread
*   what variables (or parts thereof) are offered by a thread to its thread children

如果您可以做这些事情(并且通过显式语言支持更容易,OpenMP 编译指示在此过程中帮助启用了 OpenMP 的编译器),那么您可以将线程的数据划分为 3 个部分:

1.  storage accessed only by the thread, and not by its children
2.  storage declared by the thread, but accessed only by individual children (e.g., slices of arrays)
3.  storage read and written by the parent thread and its children

此时,您可以考虑为线程布置“局部变量”,从而获得堆栈空间。线程本地存储仅分配父线程的堆栈空间。父声明但子处理的存储成为分配给子本地空间/堆栈的空间。所有人都可以读写的存储可以放在任何可以访问的地方(在父线程的本地空间中,在他的堆栈中,在堆存储中),并且需要访问保护以防止数据竞争。[你不能强迫传统的 C 编译器为你做这件事。]

将数据分区到不同的线程本地/堆栈空间使您使用 C 并将“所有线程堆栈”设置为一个的明显方案在共享内存中的位置难以驾驭。如果所有线程都具有相同的堆栈区域,那么哪个存储是线程本地的?特别是,如果两个线程都想写入自己的局部变量 I,并且 I 在共享空间中,那么它就不是真正的局部变量。如果您将共享空间划分为不相交的线程堆栈,那么您实际上并没有共享存储空间,至少没有按名称共享;充其量您可以使用指向其他线程堆栈的指针进行共享。所有这些都很难编程,因此容易出错,我不想调试为这样的系统编写的程序。它还对您宝贵的共享空间提出了额外的要求;你有线程局部变量吃掉它,但不需要共享。

如果您有静态数量的线程,并继续坚持使用一些可用的 C 编译器,您最好手动分配共享数据(在运行时动态分配,或者在编码/编译时通过分区共享内存)。但是您的线程现在可以在其本地内存中使用其“标准”堆栈运行,并且不需要堆栈切换。

[编辑:在我对仙人掌堆发表评论后,OP 想了解更多。我在这里包括评论,以及一些关于它们的详细信息]

一个栈可以被多个线程共享。仙人掌堆栈的概念是这样一种概念,其中具有现有(仙人掌)堆栈的父线程与其所有并行子线程共享该堆栈,每个子线程都获得自己的堆栈空间,但可以查看/共享父堆栈。

我们的 PARLANSE 并行编程语言直接实现了这个概念,我们在大约 2-4 百万行代码的应用程序中使用它。每个函数调用堆分配其激活记录(使用线程本地分配器以提高速度),并且可以通过词法级向上寻址访问所有父堆栈段,实现为一个由父级传递给其子级的指针盒函数称呼。下面的英特尔博客准确地描述了我们这样做的原因。

参考:

于 2013-06-21T04:09:25.420 回答
-1

为了清楚起见,让我稍微编辑一下:

你只是想改变堆栈的含义和定义。按照今天的定义,堆栈只能由一个线程用于真正的堆栈操作。堆栈可以由进程或线程共享,以便像普通内存一样使用。在这种情况下,您真的应该将那块内存称为“共享内存”而不是“共享堆栈”。您在集群环境中真正想要做什么或实现什么?

这是一个为什么不能共享堆栈的示例...

Thread-1 函数调用顺序:

main()
    thr1_f1()
        thr1_f2()
            thr1_f3()
    thr1_f4()

Thread-2 函数调用顺序:

main()
    thr2_f1()
        thr2_f2()
            thr2_f3()
    thr2_f4()

假设这些线程共享堆栈。这是一个可能的函数调用序列......

  1. 线程 1 位于 thr1_f1() 的中间,并在处理器核心 1 中进入睡眠状态。
  2. 线程 2 在 thr2_f2() 的中间开始并在处理器核心 2 中进入睡眠状态。
  3. Thread-1 出现并调用 thr1_f2()。这将覆盖 Thread-2 STACK 区域。thr2_f2() 的局部变量和参数现在搞砸了。
  4. Thread-2 将无法运行和/或在 thr2_f1() 中获取它的返回地址。根据时序,它可能会获得一些地址并转储内核。
于 2013-06-21T00:05:20.280 回答