# 尝试 Reserve & Handover 元素

### 什么是 Reserve & Handover？

Reserve 也即保留、预订，是 Vector Helper 模块中被设计用于单个或多个元素的连续预订特性；Handover 也即交出、撤销，是 Vector Helper 模块中被设计用于安全地快速撤销由 Reserve 预订的单个或多个元素的特性。

> 您依旧不明白？没关系，我们暂时暂不考虑这些特性，继续往下看场景示例。

### 为什么需要 Reserve & Handover？

例如如下场景，在函数 `thread_helper_create` 中实现创建线程，并将线程 ID 和句柄存入线程信息结构 `ThreadInfo` 中，之后将该结构体 Push 进 Vector 对象实例 `vector_object` 中。

> 以下示例代码为伪代码，仅为表达其含义，不具有可执行性。

首先我们定义 `ThreadInfo` 数据结构：

{% code title="C" %}

```c
typedef struct _ThreadInfo
{
    unsigned int thread_id;
    void* thread_handle;
} ThreadInfo;
```

{% endcode %}

然后编写 `thread_helper_create`函数：

{% code title="C" %}

```c
bool thread_helper_create(...)
{
    bool create_result = false;
    ThreadInfo thread_info = { 0 };
    thread_info.thread_handle = create_thread(..., &thread_info.thread_id);
    if(thread_info.thread_handle)
    {
        create_result = vector_helper_push_element(vector_object, &thread_info, 
            sizeof(thread_info), NULL);
        if(!create_result)
        {
            // 在此处实现安全地终止线程后关闭线程句柄
            close_handle(thread_info.thread_handle);
            printf("thread_helper_create -> vector_helper_push_element error");
        }
    }
    else printf("thread_helper_create -> create_thread error");
    return create_result;
}
```

{% endcode %}

通常情况下，函数 `thread_helper_create` 会执行成功，但处于运行时安全考虑，我们依旧需要检查函数  `vector_helper_push_element`的返回值，以确保变量 `thread_info`成功被 Push 进 Vector 对象实例。

当函数 `vector_helper_push_element` 失败时，我们便需要安全地终止之前创建的线程，这在某些平台上或许是一件非常复杂的事情，因为您不能直接调用 `terminate_thread` 来终止线程，因为这可能会造成线程所使用的内存泄漏。而线程的创建与终止涉及内核对象的创建与销毁，这会带来非常大的性能开销，一个设计良好的高性能应用程序应避免这种问题的发生。

换个角度，若我们在线程真正被创建之前，先 Push 一个空 ThreadInfo 元素，再根据线程创建成功与否来决定是填充元素（Set）还是移除元素（Remove）。的确，问题得到解决，但先后对元素进行的 Push & Set 操作造成了多次无意义的函数调用与内存拷贝开销，一个设计良好的高性能应用程序应避免这种问题的发生。

> PS：我们虽然可以通过创建挂起线程来侧面解决此问题，但并不是最优的解决方案。

若我们使用 Reserve & Handover 特性，这些问题就可以迎刃而解。我们在创建线程之前，先预订 1 个 ThreadInfo 元素，若线程创建失败则快速撤销预订。

{% code title="C" %}

```c
size_t reserve_element_index = 0;
if(vector_helper_try_reserve_element(vector_object, 1, &reserve_element_index))
{
    ThreadInfo thread_info = { 0 };
    thread_info.thread_handle = create_thread(..., &thread_info.thread_id);
    if(thread_info.thread_handle)
    {
        // 线程创建成功？设置之前预订的 ThreadInfo 元素
        create_result = vector_helper_set_element(vector_object, 
            reserve_element_index, &thread_info, sizeof(thread_info));
        // 确定之前预订的所有元素已被使用
        vector_helper_used_reserve_element(vector_object, 
            VECTOR_USED_ALL_RESERVED_ELEMENT);
    }
    else
    {
        // 线程创建失败？快速撤销之前预订的 1 个 ThreadInfo 元素
        vector_helper_handover_element(vector_object, 0, 0, false);
        printf("thread_helper_create -> create_thread error");
    }
}
else printf("thread_helper_create -> vector_helper_try_reserve_element error");
```

{% endcode %}

我们可以发现，通过使用 Reserve & Handover 特性，确保了元素与其余逻辑的操作完整性。您不必担心函数 `vector_helper_try_reserve_element` `vector_helper_used_reserve_element` `vector_helper_handover_element` 的性能开销，因为它们的执行性能最够快。

{% hint style="info" %}
您不必担心函数 vector\_helper\_set\_element 返回 false，因为 **Reserve & Handover** 特性已经确保被预订的单个或多个元素在撤销之前是可用的，因此您可以忽略 **Set** 操作的返回值。
{% endhint %}

{% hint style="warning" %}
您必须确保 **Reserve & Handover** 特性的执行顺序，不能对预订的单个或多个元素执行 **Remove** 操作，否则可能会引发不可预料的错误。
{% endhint %}

### 相关 Vector Helper API 的详细介绍

{% content-ref url="/pages/-LNJqVKACHGgB0Wcejws" %}
[元素预订与撤销](/vector/apis/reserve.md)
{% endcontent-ref %}

### 函数原型

我们在上述示例中使用了函数 `vector_helper_try_reserve_element` `vector_helper_handover_element` `vector_helper_used_reserve_element`，以下是它们的函数原型：

{% code title="C" %}

```c
bool vector_helper_try_reserve_element
(
    VectorContext* vector_object_context, 
    size_t reserve_element_count, 
    size_t* reserve_element_start_index
);

bool vector_helper_handover_element
(
    VectorContext* vector_object_context, 
    size_t reserve_element_start_index,
    size_t handover_element_count, 
    bool used_reserve_element
);

bool vector_helper_used_reserve_element
(
    VectorContext* vector_object_context, 
    size_t used_reserve_element_count
);
```

{% endcode %}

函数 `vector_helper_try_reserve_element` 的参数表如下：

| 参数名                            | 数据类型            | 必选    | 描述              |
| ------------------------------ | --------------- | ----- | --------------- |
| vector\_object\_context        | VectorContext\* | True  | Vector 对象实例描述符  |
| reserve\_element\_count        | size\_t         | True  | 需要尝试预订的元素个数     |
| reserve\_element\_start\_index | size\_t\*       | False | 接收被成功预订的首个元素索引号 |

函数 `vector_helper_handover_element` 的参数表如下：

| 参数名                            | 数据类型            | 必选    | 描述             |
| ------------------------------ | --------------- | ----- | -------------- |
| vector\_object\_context        | VectorContext\* | True  | Vector 对象实例描述符 |
| reserve\_element\_start\_index | size\_t         | False | 需要撤销的预订元素起始索引号 |
| handover\_element\_count       | size\_t         | False | 需要撤销的预订元素个数    |
| used\_reserve\_element         | bool            | True  | 被撤销的预订元素是否使用过  |

> 参数 reserve\_element\_start\_index 若为 0，则表示使用之前所预订的第一个元素作为起始索引号。
>
> 参数 handover\_element\_count 若为 0，则表示使用之前所预订的所有元素个数。
>
> 参数 used\_reserve\_element 若为 true，则在预订元素被撤销之前安全地将其从内存格式化，确保所使用的内存区块被重置为空白，防止预订元素被撤销后内存数据被读取。

{% hint style="info" %}
通常情况下，您应该确保并将参数 reserve\_element\_start\_index 和 handover\_element\_count 设置为 0，这将会触发快速撤销机制。
{% endhint %}

函数 `vector_helper_used_reserve_element` 的参数表如下：

| 参数名                           | 数据类型            | 必选    | 描述             |
| ----------------------------- | --------------- | ----- | -------------- |
| vector\_object\_context       | VectorContext\* | True  | Vector 对象实例描述符 |
| used\_reserve\_element\_count | size\_t         | False | 已使用的预订元素个数     |

> 参数 `used_reserve_element_count` 若为 0，则表示之前所预订的元素均已使用。您也可以将此参数设置为  `VECTOR_USED_ALL_RESERVED_ELEMENT` 宏。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://smallso.gitbook.io/vector/learn/4.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
