【问题标题】:Are shared pointers necessary in continuation chains?延续链中是否需要共享指针?
【发布时间】:2015-03-30 12:12:24
【问题描述】:

我有一个使用 lambda 表达式的延续链,其中一个任务分配给一个变量,下一个任务从该变量读取。 Microsoft suggests using a shared_ptr to wrap the variable even when the variable is a reference-counted handle (^). 引用计数句柄在 lambda 表达式按值捕获时不会增加其引用计数吗?那么为什么需要用shared_ptr 包装一个引用计数句柄呢?

【问题讨论】:

    标签: visual-c++ concurrency task c++-cx ppl


    【解决方案1】:

    文档清楚地表明他们关注的情况是

    延续链中的一个任务分配给一个变量,另一个任务读取该变量

    (重点是我。)这不是对象生命周期的问题,而是对象身份的问题。

    Hilo project 为例,密切关注decoder 变量(即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 定义为BitmapDecoder^,将其设置为nullptr,然后在第二个延续中为其分配一个值,则该更改不会传播到后续延续,因为更改无法反映回到初始句柄(lambda 复制了句柄,本质上是复制内存地址 0x00000000)。

    为了更新原始版本(和后续引用),您需要额外的间接(例如BitmapDecoder^*)。 shared_ptr&lt;BitmapDecoder^&gt; 就是这样一种间接方式,它很有用,因为您不需要像使用原始指针那样管理指针的生命周期,这就是文档中推荐它的原因。

    在其他情况下,捕获Object^ 就足够了,例如,如果我在我的延续之外创建了一个TextBlock^,并在第一个延续中设置它的一些属性,并在后续延续中读取一些其他属性。在这种情况下,所有句柄都指向同一个底层对象,并且没有继续尝试覆盖对象本身的标识。 (但是,正如最初提到的,这不是文档所指的用例。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-06
      • 2010-12-11
      • 2017-06-20
      • 1970-01-01
      • 1970-01-01
      • 2013-11-28
      • 1970-01-01
      相关资源
      最近更新 更多