int main() { // 使用关键字go创建协程, go后面可以使用: // 1.void(*)()函数指针, 比如:foo. // 2.也可以使用无参数的lambda, std::bind对象, function对象, // 3.以及一切可以无参调用的仿函数对象 // 注意不要忘记句尾的分号";". go foo; go []{ printf("lambda\n"); }; go std::bind(&A::fA, A()); std::function<void()> fn(std::bind(&A::fB, A())); go fn; // 也可以使用go_stack创建指定栈大小的协程 // 创建拥有10MB大栈的协程 go_stack(10 * 1024 * 1024) []{ printf("large stack\n"); }; // 协程创建以后不会立即执行,而是暂存至可执行列表中,等待调度器调度。 // co_sched是全局唯一的协程调度器,有以下接口可以调度协程: // 1.Run 执行单次调度, 返回本次执行的协程数量 // 2.RunLoop 无限循环执行Run, 不会返回 // 3.RunUntilNoTask 循环执行Run, 直至协程数量为零. // // 当仅使用一些线程进行协程调度时, 协程地执行会严格地遵循其创建顺序. co_sched.RunUntilNoTask(); return 0; }
static void fiber_accept(acl::server_socket& ss) { __fiber_accept = acl_fiber_running(); while (true) { // 等待接收客户端连接 acl::socket_stream* conn = ss.accept(); if (conn == NULL) { printf("accept error %s\r\n", acl::last_serror()); break; } // 创建处理客户端对象的协程 go_stack(STACK_SIZE) [=] { __nclients++; fiber_client(conn); }; } }
static void fiber_client(acl::socket_stream* conn) { // 创建客户端对象 user_client* client = new user_client(*conn); // 创建从客户端连接读取数据的协程 go_stack(STACK_SIZE) [=] { __nreader++; fiber_reader(client); }; // 等待该客户端连接对象退出 client->wait_exit(); printf("----- client (%s), exit now -----\r\n", client->get_name()); // 删除客户端对象及客户端连接对象 delete client; delete conn; printf("----__nclients: %d-----\r\n", --__nclients); printf("----dead fibers: %d---\r\n", acl_fiber_ndead()); }
int main() { // 使用关键字go创建协程, go后面可以使用: // 1.void(*)()函数指针, 比如:foo. // 2.也可以使用无参数的lambda, std::bind对象, function对象, // 3.以及一切可以无参调用的仿函数对象 // 注意不要忘记句尾的分号";". go foo; go []{ printf("lambda\n"); }; go std::bind(&A::fA, A()); std::function<void()> fn(std::bind(&A::fB, A())); go fn; // 也可以使用go_stack创建指定栈大小的协程 // 创建拥有10MB大栈的协程 go_stack(10 * 1024 * 1024) []{ printf("large stack\n"); }; // 协程创建以后不会立即执行,而是暂存至可执行列表中,等待调度器调度。 // co_sched是全局唯一的协程调度器,有以下接口可以调度协程: // 1.Run 执行单次调度, 返回本次执行的协程数量 // 2.RunLoop 无限循环执行Run, 不会返回 // 3.RunUntilNoTask 循环执行Run, 直至协程数量为零. // // 当仅使用一个线程进行协程调度时, 协程地执行会严格地遵循其创建顺序. co_sched.RunUntilNoTask(); // 多线程模式下, libgo还支持指定协程初始运行于哪个线程 // 使用go_dispatch关键字来创建协程, 可以分派协程执行的线程. // 支持多种分派模式 // 1.指定线程索引分派 (线程索引从0起, 按照调用Run的顺序决定线程索引) go_dispatch(2) []{ printf("dispatch to thread[2] run\n"); }; // 2.随机 (调用过Run的线程才会参与随机指派) go_dispatch(egod_random) []{ printf("random run\n"); }; // 3.robin算法 (调用过Run的线程, 或强制执行线程索引分派过协程的线程, 才会参与随机指派) go_dispatch(egod_robin) []{ printf("robin run\n"); }; // 4.尽量置于当前线程 (只有当当前线程已经调用过Run后才生效) go_dispatch(egod_local_thread) []{ printf("local thread run\n"); }; // 启动额外两个线程和主线程一起调度 boost::thread_group tg; for (int i = 0; i < 2; ++i) tg.create_thread([]{ co_sched.RunUntilNoTask(); }); co_sched.RunUntilNoTask(); tg.join_all(); // 默认配置下, 多线程调度时会采用worksteal算法做负载均衡, dispatch指定的协程也可能被其他 // 线程偷走, 如果不希望被偷走, 可以关闭worksteal算法. co_sched.GetOptions().enable_work_steal = false; // 关闭worksteal负载均衡算法 return 0; }
static void fiber_reader(user_client* client) { acl::socket_stream& conn = client->get_stream(); conn.set_rw_timeout(0); client->set_reader(); client->set_reading(true); // 登入服务器 if (client_login(client) == false) { client->set_reading(false); printf("----------client_logout-------\r\n"); // 失败,则退出客户端 client_logout(client); printf("----__nreader: %d-----\r\n", --__nreader); return; } // 登入成功,则创建写协程用来向客户端发送消息 go_stack(STACK_SIZE) [&] { __nwriter++; fiber_writer(client); }; conn.set_rw_timeout(0); bool stop = false; acl::string buf; // 从客户端循环读取消息 while (true) { bool ret = conn.gets(buf); if (ret == false) { printf("%s(%d): user: %s, gets error %s, fiber: %d\r\n", __FUNCTION__, __LINE__, client->get_name(), acl::last_serror(), acl_fiber_self()); // 客户端退出 if (client->exiting()) { printf("----exiting now----\r\n"); break; } if (errno == ETIMEDOUT) { if (conn.write("ping\r\n") == -1) { printf("ping error\r\n"); break; } } else if (errno == EAGAIN) printf("EAGAIN\r\n"); else { printf("gets error: %d, %s\r\n", errno, acl::last_serror()); break; } continue; } if (buf.empty()) continue; // 分析客户端发送的消息,交由不同的处理过程 std::vector<acl::string>& tokens = buf.split2("|"); // 本客户端要求退出 if (tokens[0] == "quit" || tokens[0] == "exit") { conn.write("Bye!\r\n"); break; } // 本客户端发送聊天消息 else if (tokens[0] == "chat") { if (client_chat(client, tokens) == false) break; } // 本客户端踢出其它客户端 else if (tokens[0] == "kick") { if (client_kick(client, tokens) == false) break; } // 要求整个服务进程退出 else if (tokens[0] == "stop") { stop = true; break; } else printf("invalid data: %s, cmd: [%s]\r\n", buf.c_str(), tokens[0].c_str()); } printf(">>%s(%d), user: %s, logout\r\n", __FUNCTION__, __LINE__, client->get_name()); client->set_reading(false); // 退出客户端 client_logout(client); printf("----__nreader: %d-----\r\n", --__nreader); if (stop) { int dumy = 1; // 如果要停止服务,则通知监控协程 __chan_monitor.put(dumy); } }