我感兴趣的方面有:
好的。我有一些使用这两个库的经验,可以澄清一些事情。
首先,从概念的角度来看,这些库在设计上有很大的不同。它们有不同的架构,因为它们的规模不同。提振。Asio是一个大型网络库,旨在与TCP/UDP/ICMP协议、POSIX、SSL等一起使用。Libuv只是一个用于Node.js的IOCP的跨平台抽象层。所以libuv在功能上是Boost的一个子集。Asio(常见功能只有TCP/UDP套接字线程,定时器)。在这种情况下,我们可以使用以下几个标准来比较这些库:
作为结论,我应该说这一切都取决于你的目的,你的项目和你具体想做什么。
提振。Asio是一个c++库,一开始专注于网络,但它的异步I/O功能已经扩展到其他资源。此外,使用Boost。Asio是Boost库的一部分,它的范围被略微缩小,以防止与其他Boost库重复。例如,Boost。Asio不会提供线程抽象,而提振。线程已经提供了。
另一方面,libuv是一个C库,被设计为node . js的平台层。它提供了Windows上的IOCP、macOS上的kqueue和Linux上的epoll的抽象。此外,它的范围似乎略有增加,包括抽象和功能,如线程、线程池和线程间通信。
每个库的核心都提供了一个事件循环和异步I/O功能。它们在一些基本特性上有重叠,比如计时器、套接字和异步操作。Libuv具有更广泛的范围,并提供了额外的功能,例如线程和同步抽象、同步和异步文件系统操作、进程管理等。相比之下,Boost。Asio最初的网络重点浮出水面,因为它提供了一组更丰富的网络相关功能,如ICMP、SSL、同步阻塞和非阻塞操作,以及用于常见任务的高级操作,包括从流读取直到接收换行符。
以下是对一些主要功能的简单并列比较。因为开发者使用Boost。Asio通常有其他可用的Boost库,我选择考虑额外的Boost库,如果它们要么直接提供,要么实现起来很简单。
libuv Boost Event Loop: yes Asio Threadpool: yes Asio + Threads Threading: Threads: yes Threads Synchronization: yes Threads File System Operations: Synchronous: yes FileSystem Asynchronous: yes Asio + Filesystem Timers: yes Asio Scatter/Gather I/O[1]: no Asio Networking: ICMP: no Asio DNS Resolution: async-only Asio SSL: no Asio TCP: async-only Asio UDP: async-only Asio Signal: Handling: yes Asio Sending: yes no IPC: UNIX Domain Sockets: yes Asio Windows Named Pipe: yes Asio Process Management: Detaching: yes Process I/O Pipe: yes Process Spawning: yes Process System Queries: CPU: yes no Network Interface: yes no Serial Ports: no yes TTY: yes no Shared Library Loading: yes Extension[2]
1. 散点/集合I/O. "
2. Boost。扩展从未提交给Boost进行审查。如前所述here,作者认为它是完整的。
而libuv和Boost。Asio提供事件循环,两者之间有一些细微的区别:
uv_default_loop()
uv_loop_new()
io_service
uv_queue_work
UV_THREADPOOL_SIZE
run
EAGAIN
EWOULDBLOCK
kill
uv_signal_t
uv_signal_*
signal_set
uv_pipe_t
local::stream_protocol::socket
local::datagram_protocol::socket
windows::stream_handle
虽然api因语言而异,但这里有几个关键的区别:
在提高。Asio,在操作和处理程序之间存在一对一的映射。例如,每个async_write操作将调用WriteHandler一次。许多libuv操作和处理程序都是如此。然而,libuv的uv_async_send支持多对一映射。多个uv_async_send调用可能导致uv_async_cb被调用一次。
async_write
uv_async_send
当处理任务时,如从流/UDP读取,处理信号,或等待计时器,Boost。Asio的异步调用链更加明确。使用libuv,创建一个观察者来指定特定事件中的利益。然后为观察者启动一个循环,其中提供了一个回调。在接收到兴趣事件时,将调用回调。另一方面,Boost。Asio要求每次应用程序有兴趣处理事件时都发出一个操作。
为了说明这种区别,这里有一个Boost的异步读循环。Asio,其中async_receive调用将被多次发出:
async_receive
void start() { socket.async_receive( buffer, handle_read ); ----. } | .----------------------------------------------' | .---------------------------------------. V V | void handle_read( ... ) | { | std::cout << "got data" << std::endl; | socket.async_receive( buffer, handle_read ); --' }
下面是libuv的相同示例,其中每当观察者观察到套接字有数据时,就会调用handle_read:
handle_read
uv_read_start( socket, alloc_buffer, handle_read ); --. | .-------------------------------------------------' | V void handle_read( ... ) { fprintf( stdout, "got data\n" ); }
由于Boost中的异步调用链。Asio和libuv中的观察者,内存分配通常发生在不同的时间。对于观察者,libuv将分配推迟到接收到需要内存处理的事件之后。分配是通过用户回调完成的,在libuv内部调用,并推迟应用程序的释放责任。另一方面,很多的Boost。Asio操作要求在发出异步操作之前分配内存,例如async_read的buffer。提振。Asio确实提供了null_buffers,它可以用来监听事件,允许应用程序推迟内存分配,直到需要内存,尽管这是不赞成的。
async_read
buffer
null_buffers
这种内存分配差异也出现在bind->listen->accept循环中。在libuv中,uv_listen创建了一个事件循环,在连接准备好接受时调用用户回调。这允许应用程序延迟客户端的分配,直到尝试连接。另一方面,Boost。Asio的listen只改变acceptor的状态。async_accept监听连接事件,并要求在被调用之前分配对等体。
bind->listen->accept
uv_listen
listen
acceptor
async_accept
不幸的是,我没有任何具体的基准数据来比较libuv和Boost.Asio。但是,我在实时和近实时应用程序中使用这些库时,观察到类似的性能。如果需要硬数字,libuv的基准测试可以作为起点。
此外,在进行概要分析以确定实际瓶颈的同时,还要注意内存分配。对于libuv,内存分配策略主要限于allocator回调。另一方面,Boost。Asio的API不允许分配器回调,而是将分配策略推给应用程序。然而,Boost中的处理程序/回调。可以复制、分配和释放Asio。提振。Asio允许应用程序提供自定义内存分配函数,以便为处理器实现内存分配策略。
Asio的开发至少可以追溯到2004年10月,经过20天的同行评审后,于2006年3月22日被纳入Boost 1.35。它也作为TR2的网络库建议的参考实现和API。提振。Asio有相当数量的文档,尽管它的有用性因用户而异。
API也有相当一致的感觉。此外,异步操作在操作名称中是显式的。例如,accept是同步阻塞,而async_accept是异步阻塞。API为常见的I/O任务提供了免费函数,例如,从流中读取直到读取\r\n。还注意隐藏一些网络特定的细节,例如ip::address_v4::any()表示0.0.0.0的“所有接口”地址。
accept
\r\n
ip::address_v4::any()
0.0.0.0
最后,Boost 1.47+提供了处理程序跟踪,这在调试时被证明是有用的,以及c++ 11支持。
根据他们的github图表,Node.js的开发至少可以追溯到2009年2月,,而libuv的开发可以追溯到3月- 2011。uvbook是介绍libuv的好地方。API文档是在这里。
总的来说,这个API相当一致且易于使用。一个可能引起混淆的异常是uv_tcp_listen创建了一个watcher循环。这与其他监视器不同,后者通常有uv_*_start和uv_*_stop对函数来控制监视器循环的生命周期。另外,一些uv_fs_*操作有相当数量的参数(最多7个)。由于同步和异步行为是在回调(最后一个参数)出现时确定的,同步行为的可见性可能会降低。
uv_tcp_listen
uv_*_start
uv_*_stop
uv_fs_*
最后,快速浏览一下libuv 提交历史就会发现开发人员非常活跃。
一个巨大的不同是Asio的作者(Christopher Kohlhoff)正在整理他的库,以包括在c++标准库中,参见http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2175.pdf和http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4370.html
添加可移植性状态:在发布这个答案时,根据我自己的尝试: