---
title: "生命周期与执行上下文"
---

> Documentation Index
> Fetch the complete documentation index at: https://openarkstudio.github.io/arknet/llms.txt
> Use this file to discover all available pages before exploring further.

# 生命周期与执行上下文

## 生命周期和执行上下文

启动前配置回调、协议选项和 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()` 约定，见
[数据库关闭与回调所有权](/arknet/zh/features/database-clients#%E9%99%90%E9%A2%9D%E4%B8%8E%E5%9B%9E%E8%B0%83%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F)。

启动、发送和停止期间，外部 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 的运行线程仍由应用负责停止和等待。

Source: https://openarkstudio.github.io/arknet/zh/concepts/lifecycle/index.mdx
