如何使用async / await异步创建DispatcherObject?

时间:2014-03-03 14:47:18

标签: c# .net asynchronous async-await

显然,async / await还有一些我不理解的东西。

以下代码有什么问题?它在异步任务中创建FDecoder对象。但在那之后,每当我尝试访问FDecoder字段时,我都会收到一个InvalidOperation异常,声明该对象归另一个线程所有。我认为这是一个很酷的事情,等待,我得到的结果回到调用线程......?

//could take very long for image from web
private Task<GifBitmapDecoder> OpenFileTask(string filename, bool forceReload = false)
{
    return Task.Run(() => 
        {
            return new GifBitmapDecoder(new Uri(filename, UriKind.RelativeOrAbsolute), forceReload ? BitmapCreateOptions.IgnoreImageCache : BitmapCreateOptions.None, BitmapCacheOption.Default);
        });
}

GifBitmapDecoder FDecoder;
public async void OpenFileAsync(string filename, bool forceReload = false)
{
    FDecoder = await OpenFileTask(filename, forceReload);
    OpenCompleted(); // do stuff with FDecoder field, throws invalid thread exception
}

修改

好的,我发现的是Task创建的实际GifBitmapDecoder对象是DispatcherObject,它具有线程关联性。这是主要问题......似乎唯一的方法是从异步任务中的Dispatcher对象中获取所有需要的数据,并传回一个没有线程关联的普通对象。但如果有人知道更好的方法,请告诉我。

4 个答案:

答案 0 :(得分:2)

您总是回到同一个上下文,但并非所有上下文都绑定到单个线程。值得注意的是,线程池上下文将所有线程池线程视为相等。

但我认为这不是具体问题 - 您使用Task.Run()来表示在线程池中运行代码。因此,即使您的await将所有内容都切换回UI上下文,也无关紧要,因为您在线程池中显式地运行了一些代码。

答案 1 :(得分:1)

这是一个有趣的问题,因为(正如你正确指出的那样)GifBitmapDecoder继承自DispatcherObject。这意味着它有一个实现,它不允许任何线程调用它的操作。

要使用任何DispatcherObject,您应该通过其Dispatcher属性进行调用。返回的Dispatcher对象允许您通过InvokeAsync以与内部线程模型兼容的方式为真实对象安排委托:

var decoder = new GifBitmapDecoder(...);
var operation = decoder.Dispatcher.InvokeAsync(() => { }); // Do things here!

这种模式,而不是返回TPL Task返回DispatcherOperation(大概是因为它早于TPL)。这个类似任务的对象允许您检查操作的状态并获得任何结果。它也是等待的,这意味着您可以像{TPL await一样使用Task

await decoder.Dispatcher.InvokeAsync(() => { });

在您的具体问题中,您应该在OpenCompleted()方法中使用此模式。您可能希望将其设为OnCompletedAsync()并返回Task以使您能够捕获连续的UI同步上下文,并让TPL处理从Dispatcher到UI的编组调用线程。

public async void OpenFileAsync(string filename, bool forceReload = false)
{
    FDecoder = await OpenFileTask(filename, forceReload);
    await OpenCompletedAsync();
}

答案 2 :(得分:0)

Task.Run()计划到线程池,因此您的GifBitmapDecoder正在另一个线程上创建。

答案 3 :(得分:-1)

OpenFileTask正在返回task<GifBitMapDecoder>。您可能需要

Task <GifBitMapDecoder> t = OpenFileTask();

Fdecoder = t.result; //Returns the GifBitMapDecoder object.

虽然对异步的东西知之甚少,但可能和你拥有的一样。

资料来源:C#5.0简而言之。