UWP/WinRT with C++/CX:在异步任务链中,如何在它们之间传递数据?
UWP/WinRT with C++/CX: In a chain of asynchronous tasks, how can data be passed between them?
我知道来自一个 lambda 的 return 被输入到下一个的参数中。但是,如果需要传递多条数据,或者程序结构已经设置了一个lambda的return类型怎么办?
这是我的工作代码,其中这两种情况都是打开文件选择器,然后以文本形式读取其内容,同时记住它来自哪个文件:
create_task(picker->PickSingleFileAsync())
.then([this](StorageFile^ file)
{
if (file == nullptr) cancel_current_task();
m_OpenFilename = file->Name;
return FileIO::ReadTextAsync(file);
})
.then([this](String^ fileContents)
{
//do something with the filename and file contents
});
请注意,为了完成这项工作,我需要添加一个 class 变量来存储异步任务之间的文件名。由于多种原因,这让我觉得很糟糕:
- 有一个 class 变量用于单个方法的内部使用是丑陋的
- 这是线程安全的吗?如果有人疯狂地打开文件选择器并选择文件,那么在访问 m_OpenFilename 时,这些异步任务是否会相互破坏?
- 这只是一个带有一个变量的简单示例,但假设我还想跟踪文件的路径、文件属性以及许多其他特征。现在,随着 class 变量数量的增加,class 看起来越来越丑陋。
我的第一个方法是在函数范围内设置一个局部变量,并将其传递给每个 lambda 函数,方法是将它们的捕获列表更改为 [this, OpenFilename]
。但是,这会失败,因为在执行 lambda 时,C++/CX 的后台内存处理程序已经丢弃 Openfilename
,导致访问它时发生访问冲突。
在我的示例中,如何将文件的元数据传递给 ReadTextAsync
的结果,以便我可以同时访问文件及其内容?
最简单的方法是继续构建嵌套延续链:
auto picker = ref new FileOpenPicker();
picker->FileTypeFilter->Append(L".txt");
picker->SuggestedStartLocation = PickerLocationId::Desktop;
auto task = create_task(picker->PickSingleFileAsync()).then(
[](StorageFile^ file)
{
auto name = file->Name;
auto task = create_task(file->OpenReadAsync()).then(
[name](IRandomAccessStreamWithContentType^ iras)
{
OutputDebugString(name->Data());
});
});
如果您不想这样做(无论出于何种原因),另一种选择是使用 shared_ptr
来保存该值;在这种情况下,我将在助手 file_info
类型中保留名称和创建日期:
struct file_info
{
Platform::String^ name;
Windows::Foundation::DateTime created;
};
auto picker = ref new FileOpenPicker();
picker->FileTypeFilter->Append(L".txt");
picker->SuggestedStartLocation = PickerLocationId::Desktop;
auto info = std::make_shared<file_info>();
auto task = create_task(picker->PickSingleFileAsync()).then(
[info](StorageFile^ file)
{
info->name = file->Name;
info->created = file->DateCreated;
return create_task(file->OpenReadAsync());
}).then(
[info](IRandomAccessStreamWithContentType^ iras)
{
OutputDebugString(info->name->Data());
OutputDebugString(L"\n");
wchar_t datetime[100];
_i64tow_s(info->created.UniversalTime, datetime, 100, 10);
OutputDebugString(datetime);
});
我知道来自一个 lambda 的 return 被输入到下一个的参数中。但是,如果需要传递多条数据,或者程序结构已经设置了一个lambda的return类型怎么办?
这是我的工作代码,其中这两种情况都是打开文件选择器,然后以文本形式读取其内容,同时记住它来自哪个文件:
create_task(picker->PickSingleFileAsync())
.then([this](StorageFile^ file)
{
if (file == nullptr) cancel_current_task();
m_OpenFilename = file->Name;
return FileIO::ReadTextAsync(file);
})
.then([this](String^ fileContents)
{
//do something with the filename and file contents
});
请注意,为了完成这项工作,我需要添加一个 class 变量来存储异步任务之间的文件名。由于多种原因,这让我觉得很糟糕:
- 有一个 class 变量用于单个方法的内部使用是丑陋的
- 这是线程安全的吗?如果有人疯狂地打开文件选择器并选择文件,那么在访问 m_OpenFilename 时,这些异步任务是否会相互破坏?
- 这只是一个带有一个变量的简单示例,但假设我还想跟踪文件的路径、文件属性以及许多其他特征。现在,随着 class 变量数量的增加,class 看起来越来越丑陋。
我的第一个方法是在函数范围内设置一个局部变量,并将其传递给每个 lambda 函数,方法是将它们的捕获列表更改为 [this, OpenFilename]
。但是,这会失败,因为在执行 lambda 时,C++/CX 的后台内存处理程序已经丢弃 Openfilename
,导致访问它时发生访问冲突。
在我的示例中,如何将文件的元数据传递给 ReadTextAsync
的结果,以便我可以同时访问文件及其内容?
最简单的方法是继续构建嵌套延续链:
auto picker = ref new FileOpenPicker();
picker->FileTypeFilter->Append(L".txt");
picker->SuggestedStartLocation = PickerLocationId::Desktop;
auto task = create_task(picker->PickSingleFileAsync()).then(
[](StorageFile^ file)
{
auto name = file->Name;
auto task = create_task(file->OpenReadAsync()).then(
[name](IRandomAccessStreamWithContentType^ iras)
{
OutputDebugString(name->Data());
});
});
如果您不想这样做(无论出于何种原因),另一种选择是使用 shared_ptr
来保存该值;在这种情况下,我将在助手 file_info
类型中保留名称和创建日期:
struct file_info
{
Platform::String^ name;
Windows::Foundation::DateTime created;
};
auto picker = ref new FileOpenPicker();
picker->FileTypeFilter->Append(L".txt");
picker->SuggestedStartLocation = PickerLocationId::Desktop;
auto info = std::make_shared<file_info>();
auto task = create_task(picker->PickSingleFileAsync()).then(
[info](StorageFile^ file)
{
info->name = file->Name;
info->created = file->DateCreated;
return create_task(file->OpenReadAsync());
}).then(
[info](IRandomAccessStreamWithContentType^ iras)
{
OutputDebugString(info->name->Data());
OutputDebugString(L"\n");
wchar_t datetime[100];
_i64tow_s(info->created.UniversalTime, datetime, 100, 10);
OutputDebugString(datetime);
});