如果你看了工程描述入门,那么是否觉得通过 add_files 添加源文件相当的方便?
目前它可以支持.c/.cpp/.s/.S/.m/.mm/.o/.obj/.a/.lib
这些后缀的源代码和库文件,其中通配符*表示匹配当前目录下文件,而**则匹配多级目录下的文件。
例如:
add_files("src/test_*.c")
add_files("src/xxx/**.cpp")
add_files("src/asm/*.S", "src/objc/**/hello.m")
add_files
的使用其实是相当灵活方便的,其匹配模式我借鉴了premake的风格,但是又对其进行了改善和增强。
使得不仅可以匹配文件,还有可以在添加文件同时,过滤排除指定模式的一批文件。。
例如:
-- 递归添加src下的所有c文件,但是不包括src/impl/下的所有c文件
add_files("src/**.c|impl/*.c")
-- 添加src下的所有cpp文件,但是不包括src/test.cpp、src/hello.cpp以及src下所有带xx_前缀的cpp文件
add_files("src/*.cpp|test.cpp|hello.cpp|xx_*.cpp")
其中分隔符之后的都是需要排除的文件,这些文件也同样支持匹配模式,并且可以同时添加多个过滤模式,只要中间用竖线分割就行了。。
注: 为了使得描述上更加的精简, 分隔符之后的过滤描述都是基于起一个模式:src/*.cpp 中 *之前的目录为基础的。
xmake的add_files接口不仅可以添加源代码文件进行编译,还可以直接添加.o/obj对象文件、以及.a/lib的库文件到编译目标中,这个跟add_links是有区别的
例如:
target("test")
-- 生成静态库:libtest.a
set_kind("static")
-- 添加对象文件
add_files("obj/*.o")
-- 添加静态库,将里面的对象文件重新打包到libtest.a中,生成新的静态库
add_files("lib/*.a")
这个target模块,可以没有任何源码,单纯的将所有静态库、对象文件重新打包到一个新的静态库中,当然再加一些源文件也是可以的
这里为了使代码更加简洁,直接用了transfer来挂接两路流的传输操作。
// 初始化文件输入流
tb_stream_ref_t istream = tb_stream_init_from_url("/home/file.txt");
// 初始化文件输出流
tb_stream_ref_t ostream = tb_stream_init_from_file("/home/file.gz", TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_BINARY | TB_FILE_MODE_TRUNC);
// 初始化解压缩流,以istream作为输入
tb_stream_ref_t fstream = tb_stream_init_filter_from_zip(istream, TB_ZIP_ALGO_GZIP, TB_ZIP_ACTION_INFLATE);
// 初始化压缩流,以istream作为输入
//tb_stream_ref_t fstream = tb_stream_init_filter_from_zip(istream, TB_ZIP_ALGO_GZIP, TB_ZIP_ACTION_DEFLATE);
// 进行流传输,并且通过 fstream进行中间外挂解压、压缩
if (istream && ostream && fstream)
{
/* 保存流数据,如果每个流都还没有调用tb_stream_open打开过
* 这里会自动帮你打开,这样上层接口使用上,看上去更加简洁明了
*
* 后面三个参数主要用于:限速、进度信息回调,这些之后再详细说明
* 现在只需要传空就行了
*
* save 是 实际传输的数据大小,失败返回:-1
*/
tb_hong_t save = tb_transfer_done(fstream, ostream, 0, tb_null, tb_null);
}
// 释放流数据
if (fstream) tb_stream_exit(fstream);
if (istream) tb_stream_exit(istream);
if (ostream) tb_stream_exit(ostream);
默认编译出来的tbox库,支持的功能比较全,带有所有模块,并且支持ssl(依赖polarssl/openssl)、gzip(依赖zlib)、database(依赖sqlite3/mysql)。
因此生成的库文件偏大,而且会依赖三个第三方库,如果你用不到上述三个模块,完全可以自己配置编译所需要的模块,减小生成库的大小。
新版采用xmake进行构建,裁剪模块已经是相当方便了,默认情况下回去自动检测依赖的第三方库进行编译。
如果要禁用某个第三方库或者模块,只要执行xmake f --xxxx=false
就行了,所有第三方库依赖都是可选,完全可以禁用。。
例如,禁用所有第三方库支持:
xmake f --polarssl=false --sqlite3=false --openssl=false --mysql=false --zlib=false
如果要最小化编译,可以禁用所有可选模块和特性:
xmake f --network=false --asio=false --charset=false --xml=false --database=false --zip=false --thread=false
tbox中使用float相关的代码,也是可以裁剪掉的,并且tbox还提供了一整套fixed16、fixed30、fixed6的定点运算库,来应付一些需要float运算的地方
TBOX的线程池通过在每个worker中批量一次拉取多个task,对锁的竞争进行了优化。
由于每个task的函数实现不会太多,所以可以根据每个task的函数地址做hash,统计出每个task执行所花费的平均时间。然后根据这个平均值来动态计算每个worker一次拉取的task的数量,TBOX里面默认每个worker一次拉取10s的task量,这样可以尽可能的避免worker间锁的频繁抢占。
所有从等待队列被拉取出来的task,都会被放到pending队列中去,如果等待队列中的task都被取完了,某个worker处于了空闲状态,就会尝试去pending中,重新拉取其他worker还没有执行到的task, 这样可以解决某些task耗时太长,将worker中剩余的task阻塞住的问题。
重新从pending队列中拉取其他worker的task,并没有通过锁来维护,而是通过原子操作判断task的状态来维护的,所以性能上还是可以保证的。
整个线程池,只用到了一个锁来维护内部的几个队列,每个worker在大部分情况都是独立运行的,只有在自己的所有task都执行完空闲时,才回去全局等待队列中取task,并且上层接口也提供了批量投递任务的接口,来最小化对锁的使用。
下面看下简单的使用例子: