- 论坛徽章:
- 44
|
本帖最后由 windoze 于 2014-03-21 17:14 编辑
简单的说就是这样,假定你有一个blocking的tcp_stream类,一个echo server大概会是这个样子:
- void servant(tcp_stream s) {
- while(!s.eof()) {
- // 从socket stream中读入一行
- std::string line;
- std::getline(s, line);
- // 向socket中写入一行,std::endl会调用stream.flush(),确保数据都被发送出去
- s << line << std::endl;
- }
- }
- int main_entry(int argc, char *argv[]) {
- // acceptor本质上就是一个listen socket
- io::tcp::acceptor acc=io::listen(12345);
- while(1) {
- //blocking accept调用,返回一个connected socket,假定它有一个简单的basic_iostream兼容包装叫tcp_stream
- tcp_stream stream(io::accept(acc));
- // 创建一个工作线程处理该socket,detach说明这个thread不需要被join
- std::thread(servant, std::move(stream)).detach();
- }
- return 0;
- }
- int main(int argc, char *argv[]) {
- std::thread(main_entry, argc, argv).join();
- return 0;
- }
复制代码 以上是一个极简单的1 connection per thread的echo server,只要并发连接数不多(比如只有几个或十几个,不超过CPU物理核数),它能很好的工作,效率远非任何非阻塞/异步模型可比。
但上面的程序也有致命问题,如果并发连接数太多,比如C10K甚至C100K的时候,CPU和OS无法有效的调度每个线程,甚至无法创建这么多的线程,导致程序效率骤降甚至崩溃。
Fiberized.IO的解决方案是采用coroutine/green thread/fiber(随便你叫什么,反正差不多),将10K甚至100K的fiber映射到一组固定尺寸的线程池上,在用户态进行调度,从程序角度看,它依然适用blocking I/O的模型,但在底层,所有blocking I/O的操作其实都是一个Async I/O的操作,通过适当的处理fiber状态并切换fiber context,保证每个工作线程的利用率最大化,减少线程争用,提高吞吐量和并发能力。
上面的程序很容易修改成fiber based,只需将std::thread换成fibio::fiber,并将main函数改成这样即可:
- #include <fibio/fiber.hpp>
- #include <fibio/stream/iostream.hpp>
- using namespace fibio;
- // 之前的程序片段
- ...
- // 4是线程池尺寸,你可以用std::thread::hardware_concurrency()代替以创建和CPU物理核数一致的线程池
- int main(int argc, char *argv[]) {
- return fiberize(4, main_entry, argc, argv);
- }
复制代码 |
|