none
关于存储InMemoryRandomAccessStream卡死问题 RRS feed

  • 问题

  •         protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                Task<IRandomAccessStream> t = Test();
                t.Wait();
                Debug.Assert(t.Result != null);
            }
    
            private async Task<IRandomAccessStream> Test()
            {
                byte[] buf = new byte[] { 1, 2, 3, 4 };
                InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
                DataWriter dataWriter = new DataWriter(ras.GetOutputStreamAt(0));
                {
                    dataWriter.WriteBytes(buf);
                    await dataWriter.StoreAsync();
                    await dataWriter.FlushAsync();
                }
                return ras;
            }
    如上代码,在metro运行,会发现没有退出Wait等待。断点在await dataWriter.StoreAsync(),一按F5,就没有然后了,一直没有任何反应。
    2012年5月15日 3:09

答案

  • 两种方式解决你的等待:

    1.

    t.ContinueWith(delegate { Debug.Assert(t.Result != null); });


    2.

            protected async override void OnNavigatedTo(NavigationEventArgs e)
            {
                Task<IRandomAccessStream> t = Test();
                await t;
                Debug.Assert(t.Result != null);           
            }

    为什么要这么做?

    http://social.msdn.microsoft.com/Forums/en/winappswithcsharp/thread/0d24419e-36ad-4157-abb5-3d9e6c5dacf1 这篇文章最后Stephen Toub - MSFT 有一个答复:

    They're different because in a console app, SynchronizationContext.Current will return null by default, and in a Metro style app, SynchronizationContext.Current will return a context for the UI thread when invoked from the UI.  Thus by default "await" when invoked from the UI thread of a Metro style app will force continuations back to the UI thread.  This is what creates the aforementioned deadlock.  Seehttp://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115163.aspx for more details on the phenomenon.

    命令行程序以及其他模式的程序中 SynchronizationContext.Current 是返回null的,但是在Metro应用中,他是返回从UI线程调用的上下文,因此默认情况下在Wait中用"await"关键词会回调到UI线程,这样就造成了死锁。


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年5月17日 8:09
    版主

全部回复

  • protected override void OnNavigatedTo(NavigationEventArgs e) {

    这个方法没有async 标签...这样wait()就会卡死。。。

    2012年5月15日 3:19
  • 不是,async是和await配对的,和Wait()方法没关系,事实上,这样用也卡死

            private void Test1()
            {
                byte[] buf = new byte[] { 1, 2, 3, 4 };
                InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
                Stream s = ras.AsStreamForRead();
                s.Read(buf, 0, buf.Length);
                s.Flush();
            }

    貌似是InMemoryRandomAccessStream的问题,但是实在找不到Metro下可以把byte数组转成IRandomAccessStream的方法了

    2012年5月15日 5:51
  • 两种方式解决你的等待:

    1.

    t.ContinueWith(delegate { Debug.Assert(t.Result != null); });


    2.

            protected async override void OnNavigatedTo(NavigationEventArgs e)
            {
                Task<IRandomAccessStream> t = Test();
                await t;
                Debug.Assert(t.Result != null);           
            }

    为什么要这么做?

    http://social.msdn.microsoft.com/Forums/en/winappswithcsharp/thread/0d24419e-36ad-4157-abb5-3d9e6c5dacf1 这篇文章最后Stephen Toub - MSFT 有一个答复:

    They're different because in a console app, SynchronizationContext.Current will return null by default, and in a Metro style app, SynchronizationContext.Current will return a context for the UI thread when invoked from the UI.  Thus by default "await" when invoked from the UI thread of a Metro style app will force continuations back to the UI thread.  This is what creates the aforementioned deadlock.  Seehttp://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115163.aspx for more details on the phenomenon.

    命令行程序以及其他模式的程序中 SynchronizationContext.Current 是返回null的,但是在Metro应用中,他是返回从UI线程调用的上下文,因此默认情况下在Wait中用"await"关键词会回调到UI线程,这样就造成了死锁。


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年5月17日 8:09
    版主
  • 参照了里面的一些文档的说明,我写了一些测试,主要是用来确认后面修改的方案

    1 就是改用await async语句改写大量的接口,因为底层的io接口是异步的,所有调用它的接口都变成了异步。

    2 使用ConfigureAwait(false),避免在UI线程上调用Wait()死锁。

    但是我在测试的过程中,发现了一些问题

    在UI线程这样用

    var t = Test1();
    t.Wait();

    这样在UI线程上不死锁

     StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync("test.t").AsTask().ConfigureAwait(false);
                return file;

    这样在ui线程上用会死锁

                byte[] buf = new byte[] { 1, 2, 3, 4 };
                InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
                Stream s = ras.AsStreamForWrite();
                s.Write(buf, 0, buf.Length);
                s.Flush();

    这样也死锁

                byte[] buf = new byte[] { 1, 2, 3, 4 };
                InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
                using (DataWriter dataWriter = new DataWriter(ras))
                {
                    dataWriter.WriteBytes(buf);
                    await dataWriter.StoreAsync().AsTask().ConfigureAwait(false);
                    await dataWriter.FlushAsync().AsTask().ConfigureAwait(false);
                    return ras;
                }
    

    这个为什么和文档说的不一样呢?是不是里面的实现还有不同?如果不同的接口会有卡死情况的话,那我就只能改接口了,免得有兼容问题。

    2012年5月21日 13:26
  • 在新版中,你的问题貌似不会出现
    2012年5月25日 1:51