【问题标题】:Deserialization and Async/Await反序列化和异步/等待
【发布时间】:2011-12-21 15:51:28
【问题描述】:

在我的应用程序(Windows 8 Metro)中,我将一些对象以序列化格式存储在本地文件夹中。这是读取然后返回的方法(见下文)。

如果我用Task.Run调用这个方法,我可以得到对象:

var entity= Task.Run<Entity>(() => GetASync<Entity>(file)).Result;

但是如果我使用 await 关键字,它就不起作用 - 在方法中的行 A (ReadObject) 上,线程停止并退出,没有错误或异常:

var entity= await GetASync<Entity>(file);

也许我没有按照建议使用 await / async?

方法

private async Task<T> GetASync<T>(IStorageFile file) where T : class
{
    try
    {    
        if (file != null)
        {
            IRandomAccessStream readStream = await file.OpenAsync(FileAccessMode.Read);
            IInputStream inputStream = readStream.GetInputStreamAt(0);                   
            using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(inputStream.AsStream(), XmlDictionaryReaderQuotas.Max))
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                var entity = serializer.ReadObject(reader); //line A - here the problem 
                return entity as T;
            }                    
        }
        else
        {
            return null;
        }

    }
    catch (FileNotFoundException)
    {
        return null;
    }
    catch (Exception)
    {
        throw;
    }
}

【问题讨论】:

  • 它对file = null这种小事有用吗?
  • 仅供参考,您的解决方法应该是 var entity= await Task.Run&lt;Entity&gt;(() =&gt; GetASync&lt;Entity&gt;(file)); - 您不想在异步代码中访问 Result,因为它处理错误的方式不同。

标签: c# windows-8 async-ctp async-await


【解决方案1】:

好吧,我不知道为什么您的代码不起作用。我怀疑会出现死锁,但不应该有一个。 :)

但是,我确实有一些性能建议可能会避免该问题的副作用。首先是使用ConfigureAwait(false)

IRandomAccessStream readStream = await file.OpenAsync(FileAccessMode.Read)
                                           .StartAsTask()
                                           .ConfigureAwait(false);

另一种是将文件读入内存(异步)然后然后解析它。 (我从您的代码中假设您为每个文件存储一个对象)。

【讨论】:

  • 谢谢斯蒂芬,第一个推荐成功了,但我不知道为什么...对于第二个,我不明白性能问题,你能解释一下吗?
  • ReadObject 是同步的。因此,它将在读取期间阻塞线程池线程(如果您取出ConfigureAwait,那么它将改为阻塞UI线程)。此外,IInputStream 包装器将同步读取实现为阻塞异步读取 - 这意味着分配了更多对象(例如,ManualResetEvent)。最好的解决方案是ReadObjectAsync,但 AFAIK 尚不存在,因此可接受的解决方案是将文件异步读入内存,然后(同步)解析它。这样你就不会在 I/O 上阻塞线程池线程。
  • 关于为什么它起作用:我怀疑ReadObject 中存在死锁,它在读取期间阻塞了 UI 线程。如果底层异步读取试图在 UI 线程上完成(它不应该这样做),就会发生死锁。我查看了 WinRT 包装器,但看不到它会在哪里执行此操作。 ConfigureAwait 通过在读取操作开始之前删除 UI 上下文来工作 - 因此底层异步读取现在将在线程池线程上完成。不过,它确实让我感到烦恼,因为它不应该是必要的。
  • 哇,感谢您的详细解释,我仍然需要阅读更多关于 async/await 的内容,Windows 8 编程是深入了解它的好方法。
  • 一定要看 Gragus 的回答;他已经确认这是一个已知的错误。我希望它会在未来的 Win8 版本中得到修复。
【解决方案2】:

来自http://social.msdn.microsoft.com/Forums/en-US/async/thread/3f192a81-073a-47ea-92e2-5ce02bf5ad33:

您在 Microsoft 在 BUILD 会议上分发的 .NET 开发人员预览版中遇到了一个已知问题。 该问题是由 WinRT UI 线程的一些细微细节引起的。因此,如果从 UI 线程执行,任何来自托管代码的阻塞 WinRT 流 IO 都将导致死锁。异步 IO(例如ReadAsync)可以正常工作。开发团队已经意识到了这个问题并正在努力解决它。

但是请注意,即使问题得到解决,在 UI 线程上执行阻塞 IO 也不是一个好主意。您的应用程序将在操作期间阻塞并无响应。一些 .NET API(还)没有异步等效项,即使有,转换代码也可能需要工作。如果必须执行阻塞 IO 操作,请确保将其卸载到线程池:

DoUIStuff();
Int32 x = await Task.Run(() => {
    OpenStream();
    PerformBlockingIO();
    ProcessResults();
    return ComputeIOResults();
});
UseIOResults(x);

这样您就可以让您的应用程序始终保持响应。作为副作用,您还将解决上述错误并避免死锁。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-21
    • 2013-12-04
    • 1970-01-01
    • 1970-01-01
    • 2019-02-21
    • 2018-07-16
    • 2013-04-12
    相关资源
    最近更新 更多