Skip to content

生命周期与执行上下文

查看 Markdown

生命周期和执行上下文

启动前配置回调、协议选项和 TLS 身份。停止完成前,保持对象及回调捕获的资源存活。 接收回调中的数据 view 只在回调期间有效;需要保留数据时,复制数据。

is_started() 和 is_stopped() 读取原子生命周期状态,对象存活期间可从其他线程查询。 结果只反映查询时的状态,不保证连接随后仍可用,也不代表可以跨线程直接操作 socket。

服务端的 get_listen_address() 和 get_listen_port() 在启动与关闭期间读取同步保护的端点快照。 直接访问 acceptor() 仍需遵守 Asio 的线程约定。

内置 IO 线程池每个通道使用一个 context 和一个工作线程。外部 context 可以由一个 或多个线程调用 run()。每个外部通道使用 strand 串行执行自身回调;下一次回调 可能运行在不同的宿主线程。独立通道可以并行执行,应用仍需同步跨通道共享的状态。

传输端点的客户端和服务端采用以下停止约定:

操作 约定
所有者线程调用 stop() 发起停止并等待完成
request_stop() 调度停止,可在网络回调中调用
所有者线程调用 wait_stopped() 完成停止,并报告是否允许等待
在所涉及 context 或线程池的工作线程调用 wait_stopped() 返回 false,错误为 operation_not_supported

对于基于 TCP 的端点,所有者线程等待完成后,已取消的原生 I/O、已受理回调的捕获对象, 以及仍借用服务端资源的 Session 任务均已退出。停止期间拒绝新增用户定时器、延迟 post 和条件等待;显式重新启动后恢复受理。此前已受理的条件回调仍会在关闭期间执行。 应用应先停止外部任务生产者:普通即时 post 仍可提交,因此回调不得无限提交后续工作。 停止后重新启动等生命周期操作须串行执行。TCP 服务端及其 HTTP、WebSocket 和 TLS 派生服务端允许多个所有者线程同时等待停止。若回调捕获对象或 Session 的析构函数 在排空期间从同一所有者线程重入停止,stop() 返回而不等待,并设置 operation_not_supported;wait_stopped() 返回 false,并设置该错误。

数据库客户端另行使用异步 close() 约定,见 数据库关闭与回调所有权。

启动、发送和停止期间,外部 context、运行线程及 work guard 必须持续运行。arknet 不会停止宿主 context。先停止并销毁网络对象,再释放宿主 work guard 并等待运行 线程退出。让 run() 执行完已排队的完成回调后自然返回。io_context::stop() 会停止回调派发,不会完成这个排空过程。所有 context 都应保持存活,直到运行 线程结束;尤其要考虑回调仍持有另一个 context 的 Session 或 strand。 不要在内置 IO 线程池自己的工作线程中销毁线程池;这种情况下析构函数会终止程序。

独立的 arknet::timer 在构造时启动,提供 stop(),没有 request_stop() 或 wait_stopped()。在回调中调用 stop() 不会阻塞;外部 context 持续运行时, 所有者线程可以再次调用 stop(),等待尚未完成的处理函数退出。

进程退出

从 main 返回前,应停止网络任务并释放其所有者。回调在内置 IO 工作线程上释放端点的最后一个引用时, arknet 会将线程池删除操作交给回收线程。进程清理会等待这些内置线程池、回调及 TLS 资源释放, 然后再进入 OpenSSL 的常规全局清理。回收线程在构造第一个内置 IO 线程池时启动,包含头文件不会启动线程。

使用全局所有者时,每个保存所有权的编译单元都应在声明对象前包含 arknet 的 IO 头文件。 这一约定也覆盖先声明空 shared_ptr、再在 main 中赋值的情况。 若其他编译单元只通过前置声明或类型擦除保存全局所有权,且没有包含该头文件, 则必须在 main 返回前显式清空所有者;arknet 无法推断这类对象的静态析构顺序。

Standalone Asio 的默认配置使用原生线程存储。若应用已先行包含 Asio,或显式选择了旧的 pthread-key 线程存储,应确保各编译单元配置一致,并在退出 main 前销毁所有内置端点和线程池。 外部 IO 的运行线程仍由应用负责停止和等待。

Navigation

Type to search…

↑↓ navigate↵ selectEsc close