我有一个使用 lambda 表达式的延续链,其中一个任务分配给一个变量,下一个任务从该变量中读取。Microsoft 建议使用 ashared_ptr
来包装变量,即使变量是引用计数句柄 (^) 也是如此。当 lambda 表达式按值捕获时,引用计数句柄不会增加其引用计数吗?那么为什么有必要用 包装一个引用计数句柄shared_ptr
?
1 回答
文档清楚地表明,他们关注的案例是
延续链中的一个任务分配给一个变量,另一个任务读取该变量
(重点是我的。)这不是对象生命周期的问题,而是对象身份的问题。
以Hilo 项目中的这个例子为例,密切关注decoder
变量(即 a shared_ptr<BitmapDecoder^>
):
task<InMemoryRandomAccessStream^> ThumbnailGenerator::CreateThumbnailFromPictureFileAsync(
StorageFile^ sourceFile,
unsigned int thumbSize)
{
(void)thumbSize; // Unused parameter
auto decoder = make_shared<BitmapDecoder^>(nullptr);
auto pixelProvider = make_shared<PixelDataProvider^>(nullptr);
auto resizedImageStream = ref new InMemoryRandomAccessStream();
auto createThumbnail = create_task(
sourceFile->GetThumbnailAsync(
ThumbnailMode::PicturesView,
ThumbnailSize));
return createThumbnail.then([](StorageItemThumbnail^ thumbnail)
{
IRandomAccessStream^ imageFileStream =
static_cast<IRandomAccessStream^>(thumbnail);
return BitmapDecoder::CreateAsync(imageFileStream);
}).then([decoder](BitmapDecoder^ createdDecoder)
{
(*decoder) = createdDecoder;
return createdDecoder->GetPixelDataAsync(
BitmapPixelFormat::Rgba8,
BitmapAlphaMode::Straight,
ref new BitmapTransform(),
ExifOrientationMode::IgnoreExifOrientation,
ColorManagementMode::ColorManageToSRgb);
}).then([pixelProvider, resizedImageStream](PixelDataProvider^ provider)
{
(*pixelProvider) = provider;
return BitmapEncoder::CreateAsync(
BitmapEncoder::JpegEncoderId,
resizedImageStream);
}).then([pixelProvider, decoder](BitmapEncoder^ createdEncoder)
{
createdEncoder->SetPixelData(BitmapPixelFormat::Rgba8,
BitmapAlphaMode::Straight,
(*decoder)->PixelWidth,
(*decoder)->PixelHeight,
(*decoder)->DpiX,
(*decoder)->DpiY,
(*pixelProvider)->DetachPixelData());
return createdEncoder->FlushAsync();
}).then([resizedImageStream]
{
resizedImageStream->Seek(0);
return resizedImageStream;
});
}
该decoder
变量首先在延续之外定义,因为在多个延续中需要它。此时,它的值为空。它是在第二个延续中获得和设置的,并且该对象(PixelWidth
等)的属性在第四个延续中使用。
如果您将decoder
其定义为 a BitmapDecoder^
,将其设置为nullptr
,然后在第二个延续中为其分配一个值,则该更改不会传播到后续延续,因为更改无法反映回初始句柄(lambda 已复制句柄,本质上是复制内存地址 0x00000000)。
为了更新原始版本(和后续引用),您需要一个额外的间接(例如 a BitmapDecoder^*
)。Ashared_ptr<BitmapDecoder^>
是一种这样的间接方式,它很有用,因为您不需要像使用原始指针那样管理指针的生命周期,这就是为什么在文档中推荐它的原因。
在其他情况下,捕获一个Object^
就足够了,例如,如果我在TextBlock^
我的延续中创建了一个外部并在第一个延续中设置它的一些属性,并在后续延续中读取一些其他属性。在这种情况下,所有句柄都指向同一个底层对象,并且没有继续尝试覆盖对象本身的标识。(但是,正如最初提到的,这不是文档所指的用例。)