0x0A-C线程和Glib的视角
C11之线程
这部分 GCC 并没有提供实现,也就是说GCC没有义务提供这个实现,我们只能用一些第三方的实现。
看不懂这一次GCC什么用意,都四年过去了。
所以现在在写跨平台多线程的程序时我一般选择使用 Qt 这个框架(C++)。
当然,C语言发展了这么多年了,自然少不了自己的第三方库,实际上标准库只提供了很小的一部分内容,甚至连某些常用的数据结构都未曾实现,我们该一直反复造轮子吗?
当然不!
在这个C的变成世界里,有许多实用的库,其中最有名的且最通用(跨多个平台的实现包括Windows,要知道很多实用的编程库都不提供Windows的实现)就是GLib这个库,其中就有实现线程的部分。
但是,因为这是中文的,看的人自然不是歪果仁,中国编程新手大都还是习惯用 Windows 环境,也不做强求,仁者见仁智者见智,后续会有一个程序作为例子,其中简单的应用了多线程的知识来写一个备份软件,线程的实现是用的 Windows 自己的接口,所有这些接口都能在 MSDN 里查找到相应文档。
Glib库在Windows下的配置
之所以不说 *nix 系统下的配置是因为,哪里的配置太无脑了,特别是Ubuntu,一句命令+有网络基本就配置完毕了。
- 使用的是稳定版的2.28.8版本,截至目前可用的最高稳定版本为2.46.x版本
- 将预处理配置好一些步骤的glib打包放在我的网盘中,可以直接下载,添加IDE的路径就能使用,这是对于 Visual C++ 系列编译器能用,如果用 MinGW 系列的编译器就需要重新编译
- 如果想自己配置,也可以前往这个网址进行下载,或者前往GNU项目主页下载最新的源码以及工程文件自我编译,方式有很多,不使用现有二进制而自行选择编译的大概莫过于想使用MinGW,在MinGW项目的主页也有介绍
如果资源太少,可以参考如何编译GTK项目的方法,因为GLib的前身便是GTK的一部分,只不过后来独立出来了。
微软的宇宙级编译器Visual Studio对于C89(C90)之后的标准并不支持,但是对其中的特性却早早进行了实现(即没有可开启标准的选项,但是新标准所说的特性它都拥有,都能够使用,甚至还要更加超前)
故接下来的备份程序将使用Visual Studio 2013 进行编写。
- 配置glib-2.28.8
- 下载编译好的二进制包,预处理好(某些操作,不多说,网上有教程,记得用谷歌,或者到博客园里找类似的,但是版本比较老可能和我用的有一些出路,但可以依着葫芦画瓢)以后,将路径配置到工程里:
- 创建一个Win32程序,并且在属性管理器(左侧栏下部寻找)中创建属性表(Debug和Release各创建一个,设置都相同即可)
- 打开新建的属性表
- 通用属性->VC++目录->包含目录->编辑 添加下载下来的文件中的
glib\glib2.28\include
目录,不放心的还可以再添加一个glib\glib2.28\lib\glib-2.0\include
目录 - 通用属性->VC++目录->库目录->编辑 添加
glib\glib2.28\lib
目录 - 通用属性->链接器->输入->附加依赖项 添加
glib\glib2.28\lib
目录下的所有.lib
文件,即将这些文件的名字都手动输入进去,如果使用我的这个版本的话那就是 gio-2.0.lib glib-2.0.lib gthread-2.0.lib gmodule-2.0.lib gobject-2.0.lib
- 通用属性->C/C++->代码生成->运行库开启
多线程/MT
- Okay!成了
休息一下
- 其实对于C程序员而言,最重要的莫过于使用一系列开源库,而不是对新标准的追求,因为越低的标准越容易跨平台,对于库而言这是先辈总结的一系列实用的数据结构和算法,甚至是实用的框架。我们不一定需要配置他们,而是从里面吸取一些他们的技术,转为自己的代码,毕竟库对于很多程序员编写的程序来说都大材小用了,但有时候又不得不使用一些必要的数据结构和算法。
- 在大学的这几年里,也许是因为不过是一个吊车尾的一本,所以我无法感受到老师教授带来的教导,但是也使得我深深的接触到了开源,开源给予了我很多,比如更开阔的编程思路,更广阔的心胸,更有进步的动力,更多的小伙伴。当然也知道自己的渺小。
是很多人(比如知乎的回答人和提问者),都提到要多观看C的源代码, 但是这对于初学者,甚至现在的我感觉也不是一件容易的事,更遑论初入门的同学了,特别是对于许多上个世纪的大神,为了节省空间以及提高效率,简直是无所不用其极!虽然某些用法能够被现代接受,但是你能在第一眼就看出来,为了构造一个红黑树节点,把树的指针和节点的颜色信息都隐藏在一个指针地址里吗?
/* 假设有一个节点的指针 p_node */
node_color = p_node->node_color & 1; /* 原理就是用最后一位bit来存储颜色 */
其中在 Linux 里,
p_node->node_color
被设定为无符号的长整形,以整数型式存储指针和颜色信息,而不是用指针类型。node_pointer = (node_type*)p_node->node_color & ~3;/* 清除最后两位上的bit的值 */
也就是清除颜色信息,留下的就是指针的值,即地址。
为什么呢,只要我么能够保证节点的创建位置是32位/64位对齐的,我们就能够保证它的最后两位/三位是空的,绝对不会被使用的。
/* 32位 */
sizeof(void*); /* 是 4 */
/* xxxx xxxx xxxx xxxx xxxx xxxx xxxx xx00 */
/* 64位 */
sizeof(void*); /* 是 8 */
/* 前方省略48位 xxxx xxxx xxxx x000 */
意思就是,对于指针而言,因为编译器要保证寻址的高效所以它在给分配地址的时候,会对齐内存中的地址,按照指针大小的倍数对齐,这就会导致不同位的程序的指针变量的值中有几个
bit
会没有使用,则用它来存储。具体的情况,网路上的详细解说十分之多,开一个头就好。但是这真的是我们一开始就应该接触的吗?
是
- 怎么说,在很多的时候,C语言给我们的函数都不够安全可靠,但是在我们无法使用新标准提供的函数的情况下(十分常见)我们该如何做呢?当然是自己写,怎么写更完美,自然是看看别人怎么写,而不是自己一抹黑的乱来,因为事实证明,自认为好的到最后都会摔一跤,虽然不是坏事。
- 最简单的做法便是用宏包裹一下,做一些预处理,或者对于宏机制不太喜欢的人会选择用一个函数进行包裹,也未尝不可。
注
- 写在最末尾,填几个前面挖的坑。
- 不知道是不是故意的,一般GNU项目的子主页面上,找不到(很难找到)对应的项目的下载地址,也就是光看着介绍如何如何牛,如何如何好用,但就是不告诉你去哪里下,这时候,首先确认你要下的这个软件的名字,然后去GNU项目首页里的程序列表里找,在哪里一定能找到,而不是在那些介绍页面乱点,结果根本找不到。
- 最典型的就是一个叫做GMP的开源软件,用来自行编译MinGW用的依赖,希望能警醒各位。
- 之所以用2.28.8而不是2.46.x是因为我实在不想自己在Windows上编译了,因为大部分时候,写程序都是在 Linux 上,所以就偷懒一下。
- 对于我的文件是不是有毒,我说有毒,有一种叫做叫你再用Windows编程的毒。
- 好吧其实我承认Visual Studio的确是宇宙无敌的编译器。
末尾
接下来的第三部分我会用一个备份程序来贯穿
- 操作系统 : Windows
- 跨平台 : 否
- API调用 :
Win32 API
- 编译器 :
Visual Studio 2013
- 语言 :
Pure C Programing Language
会在里面介绍一下,常在开源代码中看见的一些奇怪的东西,例如
#ifdef __cplusplus
extern "c" {
#endif
...
#ifdef __cplusplus
}
#endif
这到底是什么