天津南开做网站,免费注册公司怎么注册,设计图案的软件,苏州建设工程合同备案网站导读#xff1a;C语言国际标准新的新草案之前已经公布#xff0c;新标准提高了对C的兼容性#xff0c;并将新的特性增加到C语言中。此外支持多线程的功能也受到了开发者的关注#xff0c;基于ISO/IEC TR 19769:2004规范下支持Unicode#xff0c;提供更多用于查询浮点数类型…
导读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和C11标准委员会的成员他在这篇文章里从语言集的atomic操作和线程原语开始探讨了C语言的新特性在文章末尾还讨论了C11与C兼容性问题。此外在本文和它的姊妹篇里作者还描述了C11的新功能、并发性、安全性和易用性等话题。
并发 C11的标准化了可能运行在多核平台上的多线程程序的语义使用atomic变量让线程间通信更轻量。
头文件threads.h提供宏、类型以及支持多线程的函数。下面是宏、类型和枚举常量的摘要总结 宏
thread_local, ONCE_FLAG, TSS_DTOR_ITERATIONS cnd_t thrd_t, tss_t, mtx_t, tss_dtor_t, thrd_start_t, once_flag。 通过枚举常量
mtx_init: mtx_plain, mtx_recursive, mtx_timed。 线程枚举常量
thrd_timedout, thrd_success, thrd_busy, thrd_error, thrd_nomem。 条件变量的函数
call_once(once_flag *flag, void (*func)(void)); cnd_broadcast(cnd_t *cond); cnd_destroy(cnd_t *cond); cnd_init(cnd_t *cond); cnd_signal(cnd_t *cond); cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mtx, const struct timespec *restrict ts); cnd_wait(cnd_t *cond, mtx_t *mtx); 互斥函数
void mtx_destroy(mtx_t *mtx); int mtx_init(mtx_t *mtx, int type); int mtx_lock(mtx_t *mtx); int mtx_timedlock(mtx_t *restrict mtx; const struct timespec *restrict ts); int mtx_trylock(mtx_t *mtx); int mtx_unlock(mtx_t *mtx); 线程函数
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); thrd_t thrd_current(void); int thrd_detach(thrd_t thr); int thrd_equal(thrd_t thr0, thrd_t thr1); noreturn void thrd_exit(int res); int thrd_join(thrd_t thr, int *res); int thrd_sleep(const struct timespec *duration, struct timespec *remaining); void thrd_yield(void); 特定于线程的存储功能
int tss_create(tss_t *key, tss_dtor_t dtor); void tss_delete(tss_t key); void *tss_get(tss_t key); int tss_set(tss_t key, void *val);
这些标准库函数可能更适合作为易用的API的基础而不是开发平台来使用。例如使用这些低级库的函数时很容易造成数据竞争多个进程会不同步地对同一个位置的数据进行操作。C和C标准允许任何行为即使会发生争用同一个变量x哪怕会导致严重的后果。例如多字节值x可能被一个线程修改部分字节而另一个线程又会修改别的部分值撕裂或者产生一些其它的副作用。
下面一个简单的示例程序它包含一个数据竞争其中64位的整数x会同时被两个线程改动。
#include threads.h #include stdio.h #define N 100000 char buf1[N][99]{0}, buf2[N][99]{0}; long long old1, old2, limitN; long long x 0; static void do1() { long long o1, o2, n1; for (long long i1 1; i1 limit; i1) { old1 x, x i1; o1 old1; o2 old2; if (o1 0) { // x was set by this thread if (o1 ! i1-1) sprintf(buf1[i1], thread 1: o1%7lld, i1%7lld, o2%7lld, o1, i1, o2); } else { // x was set by the other thread n1 x, x i1; if (n1 0 n1 o1) sprintf(buf1[i1], thread 1: o1%7lld, i1%7lld, n1%7lld, o1, i1, n1); } } } static void do2() { long long o1, o2, n2; for (long long i2 -1; i2 -limit; --i2) { old2 x, x i2; o1 old1; o2 old2; if (o2 0) { // x was set by this thread if (o2 ! i21) sprintf(buf2[-i2], thread 2: o2%7lld, i2%7lld, o1%7lld, o2, i2, o1); } else { // x was set by the other thread n2 x, x i2; if (n2 0 n2 o2) sprintf(buf2[-i2], thread 2: o2%7lld, i2%7lld, n2%7lld, o2, i2, n2); } } } int main(int argc, char *argv[]) { thrd_t thr1; thrd_t thr2; thrd_create(thr1, do1, 0); thrd_create(thr2, do2, 0); thrd_join(thr2, 0); thrd_join(thr1, 0); for (long long i 0; i limit; i) { if (buf1[i][0] ! \0) printf(%s\n, buf1[i]); if (buf2[i][0] ! \0) printf(%s\n, buf2[i]); } return 0; }
如果你的应用已经符合C11的标准并且将它在一个32位的机器编译过了在64位机器里long long类型会占用两倍以上的存储周期你将会看到数据竞争的结果得到像下面乱码一样的输出
thread 2: o2-4294947504, i2 -21, o1 19792
传统的解决方案是通过创建一个锁来解决数据竞争。然而用atomic数据有时会更高效。加载和存储atomic类型循序渐进、始终如一。特别是如果线程1存储了一个值名为x的atomic类型变量线程2读取该值时则可以看到之前在线程1中运行的所有其它存储即使是非atomic对象。C11和C11标准还提供其他内存一致性的模型虽然专家提醒要远离它们。
新的头文件stdatomic.h提供了很多命名类和函数来操作atomic数据的大集。例如atomic_llong就是一个为操作atomic long long整数的类型。所有的整数类都提供了相似的命名。该标准包括一个ATOMIC_VAR_INIT(n)宏用来初始化atomic整数如下
之前的数据竞争的例子可以通过定义x为一个atomic_llong类型的变量解决。简单地改变一下上述例子中声明x的那行语句
#include stdatomic.h atomic_llong x ATOMIC_VAR_INIT(0);
通过使用这样的atomic变量代码在运行中不会出现任何数据竞争的问题。
注意关键字
C委员会并不希望在用户命名空间里创建新的关键字每个新的C版本都一直在避免出现不兼容之前版本C程序的问题。相比之下C委员会WG21更喜欢创造新的关键字例如C11定义一个新的thread_local关键字来指定一个线程的本地静态存储C11使用_Thread_local来代替在C11新的头文件threads.h中有一个宏定义来提供normal-looking name
#define thread_local _Thread_local
下面我将假设你已经引入例如适当的头文件所以我会显示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进化的理由。C11中对atomic的设计充分运用了模板。比如atomicT是获得任何一种类型T的简单且常用的方法。即使T是一个类或结构那么无论T*指向哪儿atomicT*都会保存类型信息但是几年来C语言的设计依然只用了几十种命名类型如上所示的atomic_llong。这样做的主要优点在于它不需要编译器做任何改变。它能够实现一个仅库的解决方案会在最低水平调用系统依赖的原生函数。然而命名类型的解决方案干挠了为C结构或者T*指针创建atomic无论多么小。主要因为B组和D组的意见WG14决定要求C11编译器为任何类型的T识别atomicT。
后来也有一个WG14内部的关于编译器识别atomicT语法的争论。一种使用atomic-parenthesis的解决方案因为和C良好的兼容性而被推动。Let _Atomic(T)成为了指定atomicT的语法。同样的源程序能够简单地在C定义宏中编译。
#define _Atomic(T) atomicT
另一种相反的观点认为应该创建新的类限制符类似于C99的_Complex的解决方案;使用atomic-space语法atomic T类型应该被写为“_Atomic T”。使用这种语法的程序型的程序无法立刻被当作C来编译。无兼容性宏本质上看起来像atomic-parenthesis的方法。
获取C11标准
新标准可以在webstore.ansi.org查看或者搜索ISO/IEC 9899:2011。现在已经有了PDF文档但是需要支付$285和C2011一样才可以使用。一旦这些标准被ANSI采纳为美国国家标准价格将下降至$30左右。
你可以定期检查此页面的部分我及时检查草案的状态如果您填写了这个Web表单将会在C11和C11被ANSI采用为标准时获得电子邮件通知。也可以通过tplumplumhall.com联系我非常感谢来自Pete BeckerC2011标准的项目编辑的建设性建议。
原文链接drdobbs.com
更多关于C11的变更可以参考维基百科 相关文章
ISO发布C语言标准新版本
C语言中史上最愚蠢的Bug
如何创建比C语言更快的编程语言