libgo是一个使用C++11编写的协作式调度的stackful协程库, 同时也是一个强大易用的并行编程库
目前支持三个平台:
Linux
MacOSX
Windows (Win7、Win8、Win10 x86 and x64 使用VS2013/2015编译) (要切换到windows分支)
使用libgo编写多线程程序,即可以像golang、erlang这些并发语言一样开发迅速且逻辑简洁,又有C++原生的性能优势,鱼和熊掌从此可以兼得。
libgo有以下特点:
-
1.提供golang一般功能强大协程,基于corontine编写代码,可以以同步的方式编写简单的代码,同时获得异步的性能,
-
2.支持海量协程, 创建100万个协程只需使用4.5GB物理内存.(真实值, 而不是刻意压缩stack得到的测试值)
-
3.支持多线程调度协程, 提供高效的负载均衡策略和协程同步机制, 很容易编写高效的多线程程序.
-
4.调度线程数支持动态伸缩, 不再有调度慢协程导致头部阻塞效应的问题.
-
5.使用hook技术让链接进程序的同步的第三方库变为异步调用,大大提升其性能。再也不用担心某些DB官方不提供异步driver了,比如hiredis、mysqlclient这种客户端驱动可以直接使用,并且可以得到不输于异步driver的性能。
-
6.动态链接和全静态链接均支持,便于使用C++11的用户静态链接生成可执行文件并部署至低版本的linux系统上。
-
7.提供Channel, 协程锁(co_mutex), 协程读写锁(co_rwmutex), 定时器等特性, 帮助用户更加容易地编写程序.
-
8.支持协程局部变量(CLS), 并且完全覆盖TLS的所有使用场景(详见教程代码sample13_cls.cpp).
-
从近两年的用户反馈情况看,有很多用户都是已经有了一个异步非阻塞模型的项目(可能是基于epoll、libuv或asio等网络库),然后需要访问MySQL这类没有提供异步Driver的DB. 常规的连接池+线程池的方案在高并发场景下的开销十分昂贵(每个连接对应一个线程才能达到最佳性能, 几千个指令周期的线程上下文切换消耗+过多的活跃线程会导致OS的调度能力急剧下降), 让许多用户难以接受.
-
鉴于此种情况, 想要使用libgo解决非阻塞模型中阻塞操作的问题,也是完全不必重构现有代码的, 全新的libgo3.0为此场景量身打造了三大利器, 可以无侵入地解决这个问题:运行环境隔离又可以便捷交互的多调度器(详见教程代码sample1_go.cpp),替代传统线程池方案的libgo协程池(详见教程代码sample10_co_pool.cpp),连接池(详见教程代码sample11_connection_pool.cpp)
-
tutorial目录下有很多教程代码,内含详细的使用说明,让用户可以循序渐进的学习libgo库的使用方法。
-
如果你发现了任何bug、有好的建议、或使用上有不明之处,可以提交到issue,也可以直接联系作者: email: 289633152@qq.com
-
Vcpkg:
如果你已经安装了vcpkg,可以直接使用vcpkg安装:
$ vcpkg install libgo
-
Linux:
1.使用CMake进行编译安装:
$ mkdir build $ cd build $ cmake .. $ sudo make install 如果希望编译可调试的版本, "cmake .." 命令执行完毕后执行: $ make debug $ sudo make install
2.动态链接glibc: (libgo放到最前面链接)
g++ -std=c++11 test.cpp -llibgo -ldl [-lother_libs]
3.全静态链接: (libgo放到最前面链接)
g++ -std=c++11 test.cpp -llibgo -Wl,--whole-archive -lstatic_hook -lc -Wl,--no-whole-archive [-lother_libs] -static
-
Windows: (目前windows只支持2.x版本,3.0的windows支持还要等一段时间)
0.切换到windows分支
1.使用git submodule update --init --recursive下载Hook子模块
2.使用CMake构建工程文件.
比如vs2015(x64): $ cmake .. -G"Visual Studio 14 2015 Win64" 比如vs2015(x86): $ cmake .. -G"Visual Studio 14 2015" 比如vs2013(x64): $ cmake .. -G"Visual Studio 12 2013 Win64"
3.使用时需要添加两个include目录:src和src/windows, 或将这两个目录下的头文件拷贝出来使用
4.如果想要执行测试代码, 需要依赖boost库. 且在cmake参数中设置BOOST_ROOT:
例如: $ cmake .. -G"Visual Studio 14 2015 Win64" -DBOOST_ROOT="e:\\boost_1_61_0"
libgo和golang一样实现了一个完整的调度器(用户只需创建协程,无需关心协程的执行、挂起、资源回收),因此有了和golang对比单线程协程调度性能的资格(功能不对等的情况下没资格做性能对比)。
libgo的调度器还实现了worksteal算法的多线程负载均衡调度,因此有了和golang对比多线程协程调度性能的资格。
测试环境:2018款13寸mac笔记本(cpu最低配)
操作系统:Mac OSX
CPU: 2.3 GHz Intel Core i5(4核心 8线程)
测试脚本:$ test/golang/test.sh thread_number
协程中尽量不要使用TLS, 或依赖于TLS实现的不可重入的库函数。
如果不可避免地使用, 要注意在协程切换后要停止访问切换前产生的TLS数据。
- 用户调用co_yield主动让出cpu.
- 竞争协程锁、channel读写
- sleep系列的系统调用
- poll, select, epoll_wait这类等待事件触发的系统调用
- DNS相关系统调用(gethostbyname系列)
- 在阻塞式socket上的connect、accept、数据读写操作
- 在pipe上的数据读写操作
connect
read
readv
recv
recvfrom
recvmsg
write
writev
send
sendto
sendmsg
poll
__poll
select
accept
sleep
usleep
nanosleep
gethostbyname
gethostbyname2
gethostbyname_r
gethostbyname2_r
gethostbyaddr
gethostbyaddr_r
以上系统调用都是可能阻塞的系统调用, 在协程中使用均不再阻塞整个线程, 阻塞等待期间CPU可以切换到其他协程执行.
在原生线程中执行的被HOOK的系统调用, 与原系统调用的行为保持100%一致, 不会有任何改变.
socket
socketpair
pipe
pipe2
close
__close
fcntl
ioctl
getsockopt
setsockopt
dup
dup2
dup3
以上系统调用不会造成阻塞, 虽然也被Hook, 但并不会完全改变其行为, 仅用于跟踪socket的选项和状态.
ioctlsocket
WSAIoctl
select
connect
WSAConnect
accept
WSAAccept
WSARecv
recv
recvfrom
WSARecvFrom
WSARecvMsg
WSASend
send
sendto
WSASendTo
WSASendMsg