C ++线程堆栈地址范围 [英] C++ threads stack address range

查看:402
本文介绍了C ++线程堆栈地址范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C ++标准是否提供有关线程堆栈的非重叠性质的保证(如以std::thread开头的内容)?特别是,是否可以保证线程在进程的地址空间中为线程堆栈具有自己的专有排他分配范围?标准中对此有何描述?

Does the C++ standard provide a guarantee about the non-overlapping nature of thread stacks (as in started by an std::thread)? In particular is there a guarantee that threads will have have their own, exclusive, allocated range in the process's address space for the thread stack? Where is this described in the standard?

例如

std::uintptr_t foo() {
    auto integer = int{0};
    return std::bit_cast<std::uintptr_t>(&integer); 
    ... 
}

void bar(std::uint64_t id, std::atomic<std::uint64_t>& atomic) {
    while (atomic.load() != id) {}
    cout << foo() << endl;
    atomic.fetch_add(1);
}

int main() {
    auto atomic = std::atomic<std::uint64_t>{0};
    auto one = std::thread{[&]() { bar(0, atomic); }};
    auto two = std::thread{[&]() { bar(1, atomic); }};

    one.join();
    two.join();
}

这个值可以打印两次相同的值吗?感觉标准应该在某处提供此保证.但不确定.

Can this ever print the same value twice? It feels like the standard should be providing this guarantee somewhere. But not sure..

推荐答案

C ++标准甚至不需要使用堆栈来实现函数调用(或在这种意义上说线程具有堆栈).

The C++ standard does not even require that function calls are implemented using a stack (or that threads have stack in this sense).

当前的C ++草案对重叠对象进行了说明:

The current C++ draft says this about overlapping objects:

如果一个对象嵌套在另一个对象中,或者如果至少一个对象是零大小的子对象并且它们是不同类型,则两个寿命不同且不是位域的对象可能具有相同的地址;否则,它们具有不同的地址,并占用不相交的存储字节.

Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types; otherwise, they have distinct addresses and occupy disjoint bytes of storage.

并在(非规范性)脚注中:

And in the (non-normative) footnote:

根据假设"规则,如果程序无法观察到差异,则允许实现将两个对象存储在同一机器地址或根本不存储对象([在您的示例中,我认为线程并没有按照预期的那样正确同步,因此integer对象的生存期不一定重叠,因此可以将两个对象放在相同的地址.

In your example, I do not think the threads synchronize properly, as probably intended, so the lifetimes of the integer objects do not necessarily overlap, so both objects can be put at the same address.

如果固定了代码以使其正确同步,并且将foo手动内联到bar中,这样当integer对象被打印时它的地址仍然存在,那么必须分配两个对象在不同的地址,因为差异 可以观察到.

If the code were fixed to synchronize properly and foo were manually inlined into bar, in such a way that the integer object still exists when its address is printed, then there would have to be two objects allocated at different addresses because the difference is observable.

但是,这些没有一个告诉您在没有编译器帮助的情况下是否可以在C ++中实现堆栈协程.现实世界中的编译器对执行环境进行了假设,这些假设未反映在C ++标准中,而仅由ABI标准隐含.与堆栈切换协程特别相关的是以下事实:执行函数时线程描述符和线程局部变量的地址不会更改(因为它们的计算成本可能很高,并且编译器会发出代码将其缓存在寄存器中或寄存器中).堆栈).

However, none of this tells you whether stackful coroutines can be implemented in C++ without compiler help. Real-world compilers make assumptions about the execution environment that are not reflected in the C++ standard and are only implied by the ABI standards. Particularly relevant to stack-switching coroutines is the fact that the address of the thread descriptor and thread-local variables does not change while executing a function (because they can be expensive to compute and the compiler emits code to cache them in registers or on the stack).

这可能会发生:

  1. 协程在线程A上运行并访问errno.

协程从线程A挂起.

协程在线程B上恢复.

协程再次访问errno.

在这一点上,线程B将访问线程A的errno值,这很可能在此点上做了完全不同的事情.

At this point, thread B will access the errno value of thread A, which might well be doing something completely different at this point with it.

如果协程仅在被挂起的同一个线程上恢复,则可以避免此问题,这是非常严格的,可能不是大多数协程库作者所想到的.最糟糕的是,在大多数情况下,在错误的线程上继续执行似乎很有效,因为一些使用了局部性很差的线程局部变量(例如errno)并不会立即导致明显的结果.越野车程序.

This problem is avoid if a coroutine is only ever be resumed on the same thread on which it was suspended, which is very restrictive and probably not what most coroutine library authors have in mind. The worst part is that resuming on the wrong thread is likely appear to work, most of the time, because some widely-used thread-local variables (such as errno) which are not quite thread-local do not immediately result in obviously buggy programs.

这篇关于C ++线程堆栈地址范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆