设置自定义提供程序示例
本主题提供如何设置自定义异步提供程序的例子。 编写自定义异步提供程序时,将通过调用 XAsyncBegin 来启动异步方法。 这个函数拥有回调参数,可实现诸如状态变化的处理、排队回调和触发完成。
struct Context
{
uint64_t result;
};
Context* context = new Context{ 0 };
// Assume that the async block is already filled out with the completion callback and task queue.
HRESULT hr = XAsyncBegin(async, context, ProviderFuncIdentity, __FUNCTION__,
[](XAsyncOp op, const XAsyncProviderData* providerData)
{
Context* context =
reinterpret_cast<Context*>(providerData->context);
switch (op)
{
case XAsyncOp::Begin:
return XAsyncSchedule(providerData->async, 0);
case XAsyncOp::Cleanup:
delete context;
break;
case XAsyncOp::GetResult:
memcpy(providerData->buffer, &context->result, sizeof(uint64_t));
break;
case XAsyncOp::DoWork:
context->result = 12345678;
XAsyncComplete(providerData->async, S_OK, sizeof(uint64_t));
break;
case XAsyncOp::Cancel:
// This call can't be canceled.
break;
}
return S_OK;
});
这里有很多需要解压缩的内容,但基本思路很直接:调用XAsyncBegin
并给它一个 提供程序回调,以处理不同的状态变化。 回想一下,异步块包含要使用的任务队列和完成回调的可选实现。 然后,回调必须处理异步任务运行时的 五种不同情况,如下表所示。
提供程序案例 | 责任 |
---|---|
Begin |
呼叫方立即呼叫 XAsyncBegin 。 此案例负责将异步工作回调安排到任务队列中。 通过调用 XAsyncSchedule 来进行处理。 它通过任务队列的工作端口报备这个回调,以DoWork 的情况再次运行。 |
Cleanup |
当异步任务结束时调用,可以删除任何为跟踪而创建的内部数据。 对于前面的示例代码,本例清理了分配用于跟踪结果数据的Context 结构。 |
GetResult |
当用户调用XAsyncGetResult时受到调用。 然后,该案例必须写入传递到案例的输出缓冲区。 因此,原始异步结果必须已保存在某个地方。 前面的示例代码将其保存在Context 结构中。从异步任务返回数据的异步 Microsoft 游戏开发工具包 API函数内部调用 XAsyncGetResult ,并将该数据转换为适合调用者的形式。 建议使用这用操作来处理结果。 |
DoWork |
通过分派工作端口的任务队列来调用。 这意味着这个回调的堆栈框架是根据任务队列的设置方式在预定线程上运行的。 任何工作都应该在这里进行。 在 XAsyncRun 的内部异步提供程序实现中,此处是执行传入工作回调的地方。 对于自定义提供程序,可以直接在回调中完成工作,或者使用一些自定义方法来封装功能。 工作完成后,必须调用 XAsyncComplete。 此函数执行两个任务。 首先,通过提供数据大小来指定是否需要任何输出数据。 如果提供了大于零的值,则会分配内部缓冲区供 GetResult 的案例写入。 其次,将完成回调的调用排入队列,以便在任务队列的完成端口上运行。 传递到XAsyncComplete 的完成状态代码成为调用 XAsyncGetStatus 返回的代码。 |
Cancel |
由外部调用XAsyncCancel 触发。 如果此案例未实现,则什么也不会发生。 若要取消,提供程序必须能够向正在执行操作的任何工作发出停止信号。 然后,DoWork 的案例必须调用状态为E_ABORT 的XAsyncComplete 。 |