C11标准委员会成员解读C语言新标准导读:C语言国际标准新的新草案之 前已经公布,新标准提高了对C++的兼容性,并将新的特性增加到C语言中。此外支持多线程的功能也受到了开发者的关注,基于ISO/IEC TR 19769:2004规范下支持Unicode,提供更多用于查询浮点数类型特性的宏定义和静态声明功能。根据草案规定,最新发布的标准草案修订了许多特 性,支持当前的编译器。(背景:C编程语言的标准化委员会(ISO/IEC JTC1/SC22/WG14)已完成了C标准的主要修订,该标准的早期版本于1999年完成,俗称为“C99”。新标准在去年年底完成,也被称为 “C11”。) 本文作者Tom Plum是Plum Hall Inc.的技术工程副总监,也是C11和C++11标准委员会的成员,他在这篇文章里从语言集的atomic操作和线程原语开始探讨了C语言的新特性,在 文章末尾还讨论了C11与C++兼容性问题。此外,在本文和它的姊妹篇里,作者还描述了C11的新功能、并发性、安全性和易用性等话题。 并发
C11的标准化了可能运行在多核平台上的多线程程序的语义,使用atomic变量让线程间通信更轻量。 头文件<threads.h>提供宏、类型以及支持多线程的函数。下面是宏、类型和枚举常量的摘要总结: 宏:
通过枚举常量:
线程枚举常量:
条件变量的函数:
互斥函数:
线程函数:
特定于线程的存储功能:
这些标准库函数可能更适合作为易用的API的基础而不是开发平台来使用。例如,使用这些低级库的函数时,很容易造成数据竞争,多个进程会不同步地对 同一个位置的数据进行操作。C(和C++)标准允许任何行为,即使会发生争用同一个变量x,哪怕会导致严重的后果。例如,多字节值x可能被一个线程修改部 分字节,而另一个线程又会修改别的部分(值撕裂),或者产生一些其它的副作用。 下面一个简单的示例程序,它包含一个数据竞争,其中64位的整数x会同时被两个线程改动。
如果你的应用已经符合C11的标准,并且将它在一个32位的机器编译过了(在64位机器里long long类型会占用两倍以上的存储周期),你将会看到数据竞争的结果,得到像下面乱码一样的输出:
传统的解决方案是通过创建一个锁来解决数据竞争。然而,用atomic数据有时会更高效。加载和存储atomic类型循序渐进、始终如一。特别是如 果线程1存储了一个值名为x的atomic类型变量,线程2读取该值时则可以看到之前在线程1中运行的所有其它存储(即使是非atomic对象)。 (C11和C++11标准还提供其他内存一致性的模型,虽然专家提醒要远离它们。) 新的头文件<stdatomic.h>提供了很多命名类和函数来操作atomic数据的大集。例如,atomic_llong就是一个 为操作atomic long long整数的类型。所有的整数类都提供了相似的命名。该标准包括一个ATOMIC_VAR_INIT(n)宏,用来初始化atomic整数,如下: 之前的数据竞争的例子可以通过定义x为一个atomic_llong类型的变量解决。简单地改变一下上述例子中声明x的那行语句:
通过使用这样的atomic变量,代码在运行中不会出现任何数据竞争的问题。 注意关键字 C委员会并不希望在用户命名空间里创建新的关键字,每个新的C版本都一直在避免出现不兼容之前版本C程序的问题。相比之下,C++委员会 (WG21)更喜欢创造新的关键字,例如:C++11定义一个新的thread_local关键字来指定一个线程的本地静态存储,C11使用 _Thread_local来代替,在C11新的头文件<threads.h>中有一个宏定义来提供normal-looking name:
下面我将假设你已经引入例如适当的头文件,所以我会显示normal-looking name。 thread_local存储类 新thread_local存储类为每个线程提供一个单独的静态存储,并且在线程运行之前初始化。但有没有保障措施来防止你捕获一个 thread_local变量的地址,并把它传递给其他线程,然后明确实现(即,不便携/不可移植),每个线程在thread_local都有它自己的 errno存储副本。 线程可选 C11已指定为多种特色为可选功能,例如:如果它明确实现了一个名为 _ _STDC_NO_THREADS_ _ 的宏,就不会提供一个头名为<threads.h>的头文件,也不可能在其中定义任何函数。 设计准则 作为通则,WG21委托Bjarne Stroustrup整体设计和进化的责任,不明白的话可以在网上搜索“camel is a horse designed by committee”。然而,有一个WG14和WG21同样遵循的设计原则:不要给任何系统编程语言比我们(C/C++)更高效的机会! 一些与会者(称他们为“A组”)认为atomic数据仍将是一个很少使用的专业性功能,但其他人(称他们为“B组”)认为,atomic数据将成为一个重要的功能,至少会在一种系统编程语言中被应用。 在过去的几十年里,很多更高级别的语言都是基于C语言创建的(Java,C#,ObjectiveC,当然,也包括C++)和C++子集或超集(如 D和嵌入式C++)。许多参与WG14和WG21的公司已经决定使用这些语言作为自己的应用的编程语言(称他们为“C组”)。这些公司之所以选择C++作 为他们的上层应用程序的语言,通常是因为C足够稳定(或者说是因为WG21控制其标准化),而选择其他语言的公司(称他们为“D组”)通常认为C是他们所 使用的高级语言非常重要的基石。 有了这些背景,我可以得出一个C11中atomic进化的理由。C++11中对atomic的设计充分运用了模板。比如 atomic<T>是获得任何一种类型T的简单且常用的方法。即使T是一个类或结构,那么无论T*指向哪 儿,atomic<T*>都会保存类型信息,但是,几年来,C语言的设计依然只用了几十种命名类型(如上所示的atomic_llong)。 这样做的主要优点在于:它不需要编译器做任何改变。它能够实现一个仅库的解决方案,会在最低水平调用系统依赖的原生函数。然而命名类型的解决方案干挠了为 C结构或者T*指针创建atomic(无论多么小)。主要因为B组和D组的意见,WG14决定要求C11编译器为任何类型的T识别atomicT。 后来也有一个WG14内部的关于编译器识别atomicT语法的争论。一种使用atomic-parenthesis的解决方案因为和C++良好的 兼容性而被推动。Let _Atomic(T)成为了指定atomicT的语法。同样的源程序能够简单地在C++定义宏中编译。
另一种相反的观点认为应该创建新的类限制符(类似于C99的_Complex的解决方案);使用"atomic-space"语法,atomic T类型应该被写为“_Atomic T”。使用这种语法的程序型的程序无法立刻被当作C++来编译。(无兼容性宏,本质上看起来像atomic-parenthesis的方法)。 获取C11标准 新标准可以在webstore.查看(或者搜索"ISO/IEC 9899:2011")。现在已经有了PDF文档,但是需要支付$285(和C++2011一样)才可以使用。一旦这些标准被ANSI采纳为美国国家标准,价格将下降至$30左右。 你可以定期检查此页面的部分,我及时检查草案的状态,如果您填写了这个Web表单,将会在C11和C++11被ANSI采用为标准时获得电子邮件通知。也可以通过tplum@联系我,非常感谢来自Pete Becker(C++2011标准的项目编辑)的建设性建议。 更多关于C11的变更可以参考维基百科 |
|