1

在 C++ 环境中使用 PPL 任务时,我完全是个菜鸟,所以我很难弄清楚以下 C# 代码的 C++ 语法是什么:

private static async Task<RandomAccessStreamReference> GetImageStreamRef()
{
    return RandomAccessStreamReference.CreateFromStream(await GetImageStream());
}

private static async Task<IRandomAccessStream> GetImageStream()
{
    var stream = new InMemoryRandomAccessStream();
    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);
    encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, width, height, 96, 96, imageBytes);
    await encoder.FlushAsync();
    return stream;
}

此 C# 代码取自Windows Store reversi Microsoft 示例代码。到目前为止,我能得到的最好的结果是:

Concurrency::task<IRandomAccessStream^> GetImageStream()
{
    auto stream = ref new InMemoryRandomAccessStream();
    task<BitmapEncoder^>(BitmapEncoder::CreateAsync(BitmapEncoder::JpegEncoderId, Stream)).then([this, stream, width, height, imageBytes](BitmapEncoder^ encoder)
    {
        encoder->SetPixelData(BitmapPixelFormat::Rgba8, BitmapAlphaMode::Ignore, width, height, 96.0, 96.0, imageBytes);
        return encoder->FlushAsync();
    }).then([this, stream]()
    {
        return stream; //Does this even make sense?
    });
    //return stream; //Not sure if I should have this here?
}

但它会产生以下编译错误:

error C4716: 'GetImageStream' : must return a value

我理解为什么会发生此错误,但我不知道如何拥有一个返回任务而在两个不同位置没有返回值的函数?我什至还没有处理 GetImageStream 。

我什至不确定我在这方面采取了正确的道路......

谢谢!

4

2 回答 2

3

你真的很亲近。您可能缺少的一个关键点是then返回一个新任务。所以then链中的最后一个决定了你的任务的类型。

auto t = task<int>([] { return 0; });
// t is task<int>

auto t = task<int>([] { return 0; })
.then([](int i) { return 3.14; });
// t is task<double>

auto t = task<int>([] { return 0; })
.then([](int i) { return 3.14; })
.then([](double d) { return "foo"; });
// t is task<const char*>

如果您只看第一行,看起来您总是有一个task<int>,但正如您所看到的,如果您立即调用它,情况不一定如此then

其次,请记住,您的函数返回的是 a task,而不是流本身。通常你会让then你的链中的最后一个返回你将从你的函数返回的任务,而不是将任务存储在局部变量中,你只是返回它。例如:

task<const char*>
get_foo()
{
    return task<int>([] { return 0; })
    .then([](int i) { return 3.14; })
    .then([](double d) { return "foo"; });
}

同样,它看起来有点奇怪,因为快速浏览会让您认为您正在返回一个task<int>. 这可以通过使用create_task而不是显式调用任务构造函数来很好地处理。它使您完全不必显式指定任务的类型。create_async此外,如果您想返回导数,它很容易更改IAsyncInfo

我一点也不熟悉BitmapEncoder,但这里有一个经过调整的代码版本可能会奏效:

Concurrency::task<IRandomAccessStream^> GetImageStream()
{
    auto stream = ref new InMemoryRandomAccessStream();
    return create_task(BitmapEncoder::CreateAsync(BitmapEncoder::JpegEncoderId, stream))
    .then([this, width, height, imageBytes](BitmapEncoder^ encoder)
    {
        // are width, height, and imageBytes member variables?
        // if so, you should only need to capture them OR "this", not both
        encoder->SetPixelData(BitmapPixelFormat::Rgba8, BitmapAlphaMode::Ignore, width, height, 96.0, 96.0, imageBytes);
        return encoder->FlushAsync();
    }).then([stream]()
    {
        // this should work fine since "stream" is a ref-counted variable
        // this lambda will keep a reference alive until it uses it
        return stream;
    });
}

唯一真正的变化是使用create_task并立即返回其结果。

我自己还在学习 PPL,但到目前为止我学到的一件事是你几乎应该总是对你创建的任何任务做一些事情。通常要做的事情是then将它变成一个新的/不同的任务,但你仍然需要对 last 返回的任务做一些事情then。通常,您只需将其退回,如上所述。有时您会将其添加到任务容器中,然后将这些任务与when_all或组合在一起when_any。有时你会get()自己调用它来等待它的结果。但关键是,如果你创建了一个任务并且没有对它做任何事情,那么可能有问题。

于 2013-05-26T13:27:24.030 回答
0

另外,如果有人关心,这里是如何在 C++ 版本中实现上面提到的 GetImageStreamRef:

task<RandomAccessStreamReference^> GetImageStreamRef()
{
    return GetImageStream().then([](IRandomAccessStream^ pStream)
    {
        return RandomAccessStreamReference::CreateFromStream(pStream);
    });
}

此外,请确保永远不要在任何这些任务上使用 .get(),否则您将收到一个异常,提示“非法等待 Windows 运行时 STA 中的任务”。获取此图像流引用的值的正确方法是,例如在 DataRequestHandler 上,它在 DataRequest 上设置位图数据:

void OnBitmapRequested(DataProviderRequest^ request)
{
    auto Deferral = request->GetDeferral();
    GetImageStreamRef().then([Deferral, request](RandomAccessStreamReference^ pStreamRef)
    {
        try
        {
            request->SetData(pStreamRef);
            Deferral->Complete();
        }
        catch( std::exception ex )
        {
            Deferral->Complete();
            throw ex;   //Propagate the exception up again
        }
    });
}
于 2013-05-29T20:06:25.933 回答