尝试 Reserve & Handover 元素

本页将介绍如何对指定 Vector 对象实例进行元素的 Reserve & Handover 操作

什么是 Reserve & Handover?

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

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

为什么需要 Reserve & Handover?

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

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

首先我们定义 ThreadInfo 数据结构:

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

然后编写 thread_helper_create函数:

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;
}

通常情况下,函数 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 元素,若线程创建失败则快速撤销预订。

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");

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

您不必担心函数 vector_helper_set_element 返回 false,因为 Reserve & Handover 特性已经确保被预订的单个或多个元素在撤销之前是可用的,因此您可以忽略 Set 操作的返回值。

您必须确保 Reserve & Handover 特性的执行顺序,不能对预订的单个或多个元素执行 Remove 操作,否则可能会引发不可预料的错误。

相关 Vector Helper API 的详细介绍

函数原型

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

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
);

函数 vector_helper_try_reserve_element 的参数表如下:

函数 vector_helper_handover_element 的参数表如下:

参数 reserve_element_start_index 若为 0,则表示使用之前所预订的第一个元素作为起始索引号。

参数 handover_element_count 若为 0,则表示使用之前所预订的所有元素个数。

参数 used_reserve_element 若为 true,则在预订元素被撤销之前安全地将其从内存格式化,确保所使用的内存区块被重置为空白,防止预订元素被撤销后内存数据被读取。

通常情况下,您应该确保并将参数 reserve_element_start_index 和 handover_element_count 设置为 0,这将会触发快速撤销机制。

函数 vector_helper_used_reserve_element 的参数表如下:

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

Last updated