c11中的多线程支持

73
新的C11标准提供了对多线程的支持。 我的问题有些多样化,但绝对可回答。 我已经看过了C11的n1570草案。 它说:
支持多个执行线程,包括改进的内存排序模型、原子对象和线程本地存储(和)
那么,什么是“改进的内存排序模型”?与C99标准相比有何变化?
我希望有人能深入探讨这些标准,并尝试解释涉及的语义。
据我所知,C11提供以下支持:
- 线程创建和管理 - 互斥锁 - 条件变量 - 线程特定存储 - 原子对象
我希望没有遗漏任何事情?由于标准库本身提供(将提供)了多线程所需的所有功能,未来将不再需要POSIX和其他库(用于多线程支持)?
最后,哪些编译器支持上述功能?是否有关于何时支持这些功能的时间表的参考资料? 我记得对于C++11,有一个链接列出了编译器支持和功能,或许有类似的东西?

21
这个问题涉及C11而不是C++11。 两者是不同的。 您提供的链接都是关于C++11而不是C11。 明白吗? - Alok Save
3
我正在进行“完全重复”关闭投票,因为您的第一个问题(关于排序模型)已经得到了回答。 回答包含多个问题的问题很困难,因为有人可能只知道您问题的一部分 - 他们的答案因此永远不能被接受,因为它无法回答整个问题,并且可能永远不会有被接受的答案。 请拆分您的问题,并删除完全重复的部分(或说明它与我链接的问题有何不同)。 - bdonlan
2
哦,我现在明白了,这是关于C11而不是C++11的。那就算了吧!不过我仍然建议您将问题分开提出 - 您有以下问题:1)什么是内存序列模型?(可能由C++11回答,答案将类似但语法不同)2)C11是否提供了所有这些?3)是否仍需要pthreads等?4)C11编译器支持进展如何? - bdonlan
1
@bdonlan:我认为问题1-3在逻辑上相关,并且应该被组合在一起。对于其中任何一个问题的回答都会涉及到它们中的所有三个问题。也许,问题4可以是一个独立的问题,但我觉得没有必要专门为此开启一个问题。 - Alok Save
投票关闭,因为问题过于宽泛:一个问题中包含多个问题。实现状态询问于:https://dev59.com/mF7Va4cB1Zd3GeqPOfHZ和https://dev59.com/gmAf5IYBdhLWcg3wgzDI。 - Ciro Santilli OurBigBook.com
显示剩余3条评论
3个回答

58

首先,不要忽视C++11。新标准的并发工作是在C++11的支持下完成的,然后被导入C11中以实现兼容性。尽管存在一些语法差异(例如由于普通C没有模板或函数重载而产生的差异),但它们从设计上来说是相同的。为此,“证据”可以查看WG14文件。例如:

以及其中的参考文献。更多信息可以在

Open Std 网站

现在,进入您的问题:

什么是改进的内存序列模型?

显而易见的答案是,它已经被更改以考虑多个线程及其相互交互的情况。有关稍长的答案,请参见评论中已经提到的C++11引入了一种标准化的内存模型。这意味着什么?它将如何影响C++编程?。对于深入理解来说,stackoverflow的回答可能不是正确的地方(更不用说有几个子问题的问题了!)。但幸运的是,Hans Boehm维护了一个非常好的页面,其中包含有趣的链接供进一步阅读(再次记住,C11和C++11内存模型在语义上是相同的)。

我希望我没有漏掉什么?

除了内存模型之外,您的列表似乎涵盖了C11中的并发添加。对于其他更改,维基百科上有一个列表;就我个人而言,我想不出维基百科列表漏掉了什么。

由于现在标准库本身提供(将要提供)了所有需要的多线程功能,因此将来不再需要POSIX和这样的库(用于多线程支持)吗?

是的,它们将是必要的。首先,没有人会重新编写所有使用各种现有线程 API 的现有代码。其次,C(++)11 线程库很可能会作为各种本机线程库的包装器实现/将被实现; 哪怕有一种记录方式来检索指向底层本机线程的指针,以防需要执行超出 C(++) 线程库支持的操作。将 C(++)11 线程库视为对各种本机线程库最小公共分母的可移植包装器。

最后,哪些编译器提供了上述功能的支持?是否有时间表的参考?我记得对于 C++11 有一个链接用于编译器支持和功能,可能有类似的东西吗?

我没有看到任何详细的列表,与 C++11 相比,C11 的关注度似乎不高。这里有即将推出的 GCC 4.7 的简短通知: http://gcc.gnu.org/gcc-4.7/changes.html . 对于并发支持,可以在这里检查 C++11 状态页面中的并发支持: http://gcc.gnu.org/projects/cxx0x.html . 还可以在这里找到有关当前状态和 GCC 计划的一些说明:http://gcc.gnu.org/wiki/Atomic (根据该页面,stdatomic.h 可用)。对于其他编译器,这里有一个很好的 C++11 状态列表,涵盖了各种编译器的支持情况:http://www.aristeia.com/C++11/C++11FeatureAvailability.htm 。从这些链接中,人们可以检查并发支持的状态,并假定相关供应商计划支持 C11,那么 C11 并发支持很可能与 C++11 并发支持处于相同级别。


谢谢!有几点评论,我并不完全相信C11中的并发性是受到C++11的启发或引入的论点。如果有可靠的参考资料支持这一点,那么我会更加愿意接受它。关于未来需要POSIX和多线程库的问题,用户将不必担心确保这些库已经就位,因为标准现在要求实现必须将它们放置在适当的位置。因此,即使标准库API只是本地库的包装器,实现的责任也是提供整个软件包,而不像以前那样。虽然编译器支持的链接很好。 - Alok Save
@Als: 我添加了一些关于并发工作的C++遗产的链接。关于你提到的第二点,请记住:1)要等很长时间,直到C11变得普及到可以假设它已经可用的程度;2)即使如此,线程和原子是标准的可选部分,例如,请参见 <code>STDC_NO_THREADS</code> 和 <code>STDC_NO_ATOMICS</code> 宏。话虽如此,我认为内存模型是一个很大的进步,它指定了编译器应该如何处理多个线程对内存访问,这有助于使用pthread与使用C11线程的程序。 - janneb
@Als:添加了一个链接(n1349)。同时,之前评论的格式有误,非常抱歉。 - janneb
有没有标准的方法来请求“编译器内存访问”屏障,需要编译器确保在执行通过屏障之前发出所有待处理写入到底层平台,并且所有读取检查自屏障后存在的值?许多平台都有多个可用的编译器,对于为特定平台编写的程序能够可靠地在该平台的所有编译器上运行将会很有帮助,创建编译器内存排序屏障的概念似乎并不模糊。 - supercat
从Visual Studio 2017开始,有一个名为xthreads.h的头文件(您可以在包含目录中的thr文件夹下找到)。 它与threads.h几乎相同。 但我认为这只是为了支持C ++线程实现而创建的。 尽管如此,它仍然可以正常工作。 - annoying_squid
显示剩余2条评论

8
关于“哪些编译器提供上述功能的支持?”
Pelles C支持C11中的<threads.h>。使用Pelles C编译器创建线程的示例:
#include <stdio.h>
#include <threads.h>

#define NUM_THREADS 7

static int threadData[NUM_THREADS];

int threadFunction(void * data) {
    printf("%d-th thread up\n", *(int*)data);
    return 0;
}

int main(void) {
    thrd_t threadId[NUM_THREADS];

    // init thread data
    for (int i=0; i < NUM_THREADS; ++i)
        threadData[i] = i;

    // start NUM_THREADS amount of threads
    for (int i=0; i < NUM_THREADS; ++i) {
        if (thrd_create(threadId+i, threadFunction, threadData+i) != thrd_success) {
            printf("%d-th thread create error\n", i);
            return 0;
        }
    }

    // wait until all threads terminates
    for (int i=0; i < NUM_THREADS; ++i)
        thrd_join(threadId[i], NULL);

    return 0;
}

编辑:解决了线程共享数据问题和在所有线程终止之前提前退出 main() 的问题。


3

Janneb已经给出了很多解释。对于你的最后一个问题

最后,哪些编译器支持上述功能?有没有关于支持这些功能的时间表的参考资料?

gcc编译器家族(clang、icc、opencc)支持新标准所需的大多数语义,只有语法上的区别。(clang甚至在最新版本中实现了_Generic。)

对于P99,我编写了包装宏,将大多数特性映射到已经是C11语法或接近C11语法的东西上(用于模拟_Generic)。

因此,如果你有其中的一个编译器,并且在一个POSIX系统上,你可以立即开始使用C11的大部分功能:线程和所有类型的mtx_h等,原子操作_Atomic,类型通用宏(与C11语法略有不同),_Static_assert和对齐等内容。


1
“threads.h” 在我看来将成为 C 库(如 glibc)的一部分,而不仅仅是编译器。目前没有任何 C 库和编译器能够完全支持“真正”的多线程(截至 02/2k12 的现在和不久的未来)。 - Tomas Pruzina
@AoeAoe,P99还包含了对POSIX线程的“threads.h”部分的完整仿真,包括mtx_tcond_t等等。 - Jens Gustedt
2
“gcc家族”是什么定义?据我所知,GNU C编译器、Intel C编译器和Clang之间没有任何关系。 - bames53
7
@bames53,这里有一个简单的关系,这些编译器似乎大部分都试图与gcc兼容。特别地,它们都会将__GNUC__宏定义为某个值。 - Jens Gustedt

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