导读: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>提供宏、类型以及支持多线程的函数。下面是宏、类型和枚举常量的摘要总结:

宏:

  1. thread_local, ONCE_FLAG, TSS_DTOR_ITERATIONS cnd_t thrd_t, tss_t, mtx_t, tss_dtor_t, thrd_start_t, once_flag。

通过枚举常量:

  1. mtx_init: mtx_plain, mtx_recursive, mtx_timed。

线程枚举常量:

  1. thrd_timedout, thrd_success, thrd_busy, thrd_error, thrd_nomem。

条件变量的函数:

  1. call_once(once_flag *flag, void (*func)(void));
  2. cnd_broadcast(cnd_t *cond);
  3. cnd_destroy(cnd_t *cond);
  4. cnd_init(cnd_t *cond);
  5. cnd_signal(cnd_t *cond);
  6. cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mtx, const struct timespec *restrict ts);
  7. cnd_wait(cnd_t *cond, mtx_t *mtx);

互斥函数:

  1. void mtx_destroy(mtx_t *mtx);
  2. int mtx_init(mtx_t *mtx, int type);
  3. int mtx_lock(mtx_t *mtx);
  4. int mtx_timedlock(mtx_t *restrict mtx;
  5. const struct timespec *restrict ts);
  6. int mtx_trylock(mtx_t *mtx);
  7. int mtx_unlock(mtx_t *mtx);

线程函数:

  1. int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
  2. thrd_t thrd_current(void);
  3. int thrd_detach(thrd_t thr);
  4. int thrd_equal(thrd_t thr0, thrd_t thr1);
  5. noreturn void thrd_exit(int res);
  6. int thrd_join(thrd_t thr, int *res);
  7. int thrd_sleep(const struct timespec *duration, struct timespec *remaining);
  8. void thrd_yield(void);

特定于线程的存储功能:

  1. int tss_create(tss_t *key, tss_dtor_t dtor);
  2. void tss_delete(tss_t key);
  3. void *tss_get(tss_t key);
  4. int tss_set(tss_t key, void *val);

这些标准库函数可能更适合作为易用的API的基础而不是开发平台来使用。例如,使用这些低级库的函数时,很容易造成数据竞争,多个进程会不同步地对同一个位置的数据进行操作。C(和C++)标准允许任何行为,即使会发生争用同一个变量x,哪怕会导致严重的后果。例如,多字节值x可能被一个线程修改部分字节,而另一个线程又会修改别的部分(值撕裂),或者产生一些其它的副作用。

下面一个简单的示例程序,它包含一个数据竞争,其中64位的整数x会同时被两个线程改动。

  1. #include <threads.h>
  2. #include <stdio.h>
  3. #define N 100000
  4. char buf1[N][99]={0}, buf2[N][99]={0};
  5. long long old1, old2, limit=N;
  6. long long x = 0;
  7. static void do1()  {
  8. long long o1, o2, n1;
  9. for (long long i1 = 1; i1 < limit; ++i1) {
  10. old1 = x, x = i1;
  11. o1 = old1;  o2 = old2;
  12. if (o1 > 0) { // x was set by this thread
  13. if (o1 != i1-1)
  14. sprintf(buf1[i1], "thread 1: o1=%7lld, i1=%7lld, o2=%7lld",
  15. o1, i1, o2);
  16. } else {      // x was set by the other thread
  17. n1 = x, x = i1;
  18. if (n1 < 0 && n1 > o1)
  19. sprintf(buf1[i1], "thread 1: o1=%7lld, i1=%7lld, n1=%7lld",
  20. o1, i1, n1);
  21. }
  22. }
  23. }
  24. static void do2()  {
  25. long long o1, o2, n2;
  26. for (long long i2 = -1; i2 > -limit; --i2) {
  27. old2 = x, x = i2;
  28. o1 = old1; o2 = old2;
  29. if (o2 < 0) { // x was set by this thread
  30. if (o2 != i2+1)
  31. sprintf(buf2[-i2], "thread 2: o2=%7lld, i2=%7lld, o1=%7lld",
  32. o2, i2, o1);
  33. } else {      // x was set by the other thread
  34. n2 = x, x = i2;
  35. if (n2 > 0 && n2 < o2)
  36. sprintf(buf2[-i2], "thread 2: o2=%7lld, i2=%7lld, n2=%7lld",
  37. o2, i2, n2);
  38. }
  39. }
  40. }
  41. int main(int argc, char *argv[])
  42. {
  43. thrd_t thr1;
  44. thrd_t thr2;
  45. thrd_create(&thr1, do1, 0);
  46. thrd_create(&thr2, do2, 0);
  47. thrd_join(&thr2, 0);
  48. thrd_join(&thr1, 0);
  49. for (long long i = 0; i < limit; ++i) {
  50. if (buf1[i][0] != '\0')
  51. printf("%s\n", buf1[i]);
  52. if (buf2[i][0] != '\0')
  53. printf("%s\n", buf2[i]);
  54. }
  55. return 0;
  56. }

如果你的应用已经符合C11的标准,并且将它在一个32位的机器编译过了(在64位机器里long long类型会占用两倍以上的存储周期),你将会看到数据竞争的结果,得到像下面乱码一样的输出:

  1. thread 2: o2=-4294947504, i2=    -21, o1=  19792

传统的解决方案是通过创建一个锁来解决数据竞争。然而,用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的那行语句:

  1. #include <stdatomic.h>
  2. atomic_llong x = ATOMIC_VAR_INIT(0);

通过使用这样的atomic变量,代码在运行中不会出现任何数据竞争的问题。

注意关键字

C委员会并不希望在用户命名空间里创建新的关键字,每个新的C版本都一直在避免出现不兼容之前版本C程序的问题。相比之下,C++委员会(WG21)更喜欢创造新的关键字,例如:C++11定义一个新的thread_local关键字来指定一个线程的本地静态存储,C11使用_Thread_local来代替,在C11新的头文件<threads.h>中有一个宏定义来提供normal-looking name:

  1. #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进化的理由。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++定义宏中编译。

  1. #define _Atomic(T)    atomic<T>

另一种相反的观点认为应该创建新的类限制符(类似于C99的_Complex的解决方案);使用"atomic-space"语法,atomic T类型应该被写为“_Atomic T”。使用这种语法的程序型的程序无法立刻被当作C++来编译。(无兼容性宏,本质上看起来像atomic-parenthesis的方法)。

获取C11标准

新标准可以在webstore.ansi.org查看(或者搜索"ISO/IEC 9899:2011")。现在已经有了PDF文档,但是需要支付$285(和C++2011一样)才可以使用。一旦这些标准被ANSI采纳为美国国家标准,价格将下降至$30左右。

你可以定期检查此页面的部分,我及时检查草案的状态,如果您填写了这个Web表单,将会在C11和C++11被ANSI采用为标准时获得电子邮件通知。也可以通过tplum@plumhall.com联系我,非常感谢来自Pete Becker(C++2011标准的项目编辑)的建设性建议。

原文链接:drdobbs.com

更多关于C11的变更可以参考维基百科


相关文章:

ISO发布C语言标准新版本

C语言中史上最愚蠢的Bug

如何创建比C语言更快的编程语言?

C11标准委员会成员解读C语言新标准相关推荐

  1. c语言c11标准 pdf,C语言新标准C11

    这是2011年制定的一个新标准,顺便翻译一下第一章的Scope. 为什么要翻译第一章?因为我发现,过去的C语言题库中总喜欢拿C语言标准中未定义的行为和未指定的行为来说事,比如函数参数传值顺序问题-- ...

  2. c语言新标准1983年,C语言的标准

    (1)K&R C 开始的很多年,C语言没有国际标准,只有一个事实标准--1978年Brian Kemighan和Dennis Ritchie编写的<C程序设计语言>(The C P ...

  3. 解读欧盟新标准EN ISO 20345:2022《个体防护装备 安全鞋》

    安全鞋是劳保防护用品中不可或缺的一部分,安全鞋是劳保防护用品中不可或缺的一部分,企业为工作人员选择安全鞋应符合法律标准,适合其工作性质及工作环境.今天百华小编就和大家来了解下安全鞋欧盟新标准EN IS ...

  4. 欧盟玩具新标准IEC/EN62115

    EN IEC 62115:2020+A11:2020欧盟电玩具标准已正式生效 今天 2020年2月,欧洲电工技术标准化委员会(CENELEC)发布了新版的欧盟电子玩具标准EN IEC 62115:20 ...

  5. 欧洲统一语言参考标准C1,浅述欧洲统一语言参考标准.doc

    浅述欧洲统一语言参考标准 浅述欧洲统一语言参考标准 摘 要:随着2001年欧洲统一语言参考标准的问世,它已经成为了整个欧洲在外语教学上的指导性文件,并逐步被世界其他国家引入到自己国家的外语教学中.中国 ...

  6. vue标准时间改为时间戳_2021考研网上确认照片采集新标准公布 网上确认时间表...

    2021研究生招生考试网上报名10月31日即将截止,接下来就要进入现场确认环节了,据今年各大高校和省市的通告,大部分地区2021考研现场确认改为网上确认,大家不用再"行走在冬日的冷风中&qu ...

  7. C语言三大标准C89,C99和C11

    C89 标准 1983 年美国国家标准局(American National Standards Institute,简称 ANSI)成立了一个委员会,专门来制定C语言标准.1989 年C语言标准被批 ...

  8. c语言c99标准_C语言的三套标准:C89、C99和C11

    我们今天使用的 Windows.Linux.Mac OS 等操作系统都是由一种叫做 Unix 的系统演化而来.Unix 作为80年代主流的操作系统,是整个软件工业的基础,是现代操作系统的开山鼻祖,C语 ...

  9. TB/T3139-2021机车车辆有害物质限量新标准解读

    TB/T 3139标准为铁路机车材料环保标准,是测量机车车辆内装材料有害物质限量的标准. 国家铁路局于2021年3月5日批准发布TB/T 3139-2021<机车车辆非金属材料及室内空气有害物质 ...

最新文章

  1. 安装 Python MySQL 驱动(mysql-connector-python、MySQL-python)
  2. 情感分析基于词典(算例代码)
  3. 最像windows10的linux,Linuxfx:外观神似Win10的Linux操作系统
  4. 团队-中国象棋-最终程序
  5. Ext 3.0 +ASP.NET2.0 可视化开发介绍
  6. .net core精彩实例分享 -- 网络编程
  7. Oracle文件路径——.dbf与.ora
  8. MySQL新增用户以及数据库访问授权
  9. 【2020年CSDN技术人内推活动开始啦】多家名企员工在线内推,快人一步拿Offer
  10. aria2 配置教程
  11. h5将word转为html,怎么把Word版通知转化成H5版
  12. html特殊符号对照表
  13. 查询-非等值连接,外连接,子查询
  14. c语言步长,(转+原创)c语言那些细节之a+1和a+1的区别 ,指针的步长问题。
  15. python绘制箭头_python如何绘制坐标箭头?
  16. Codeforces 1077B Disturbed People(思维题)
  17. 专题2:matlab矩阵处理
  18. 类设计原则及设计模式(一篇就够)
  19. 网页制作用html和sc,实验二:html的基本标签和javasc
  20. linux查看exif软件,EXIF信息查看器使用教程及功能解析

热门文章

  1. 【转】3.4(译)构建Async同步基元,Part 4 AsyncBarrier
  2. linux – syslog,rsyslog和syslog-ng之间有什么区别?
  3. c语言删除文件remove_Python中的文件和目录操作
  4. PWN-PRACTICE-BUUCTF-30
  5. 【HDU - 2200】Eddy's AC难题(简单组合数学)
  6. 吴恩达机器学习作业(1):线性回归
  7. 0.Overview----Machine Learning
  8. java的et5_Javascript与java相同的3des加密(使用etdesede/CBC/PKCS5Padding )
  9. aosp 本地版本管理_谈 DevOps 平台实施:我在本地跑明明成功的,为什么在你平台跑就报错?...
  10. TCP局域网 通讯 的消息发送