在UWP中赢得10个IOT线程死锁

时间:2018-11-23 19:32:22

标签: c# asynchronous uwp raspberry-pi windows-10-iot-core

我想做什么: -在第一页加载之前从USB驱动器同步(甚至异步)加载设置

我做了什么:  -在App.xaml.cs的OnLaunched方法中,我调用了此静态函数:

public static async void LoadSettings(string folderName = "Config", string fileName = "Settings.xml")
{
    try
    {
    StorageFile configFile = null;
    // scan through all devices
    foreach (var device in await KnownFolders.RemovableDevices.GetFoldersAsync().AsTask().ConfigureAwait(false))
    {
        // folder that should have configuration
        var configFolder = await device.GetFolderAsync(folderName).AsTask().ConfigureAwait(false);

        if (configFile != null && configFolder != null && await configFolder.GetFileAsync(fileName).AsTask().ConfigureAwait(false) != null)
        {
            throw new Exception("More than one configuration file detected. First found configuration file will be used.");
        }
        else
            configFile = await configFolder.GetFileAsync(fileName).AsTask().ConfigureAwait(false);
    }

    if (configFile == null)
        throw new Exception("Configuration file was not found, please insert device with proper configuration path.");

    string settingString = await FileIO.ReadTextAsync(configFile).AsTask().ConfigureAwait(false);

    XmlSerializer serializer = new XmlSerializer(typeof(Settings));
    using (TextReader reader = new StringReader(settingString))
    {
        AppSettings = (Settings)serializer.Deserialize(reader); // store settings in some static variable
    }
}
catch (Exception e)
{

    //return await Task.FromResult<string>(e.Message);
}

//return await Task.FromResult<string>(null);
}

您现在可以看到,它是一个异步void方法,所以我什至不想与UI线程进行任何同步。它应该开火并做点什么。使用ConfigureAwait(false),我想确保它永远不会尝试返回上下文。最后的这些回报是我尝试过的其他事情的残余(我想做这种更好的方法,这是最原始的解决方案,但仍然行不通)。

无论如何,因为这就是乐趣的开始:当我使用Win 10在本地计算机上调试应用程序时,一切都很好,而且在Raspberry Pi 3上安装的Win 10 IOT上出现死锁线程(我今天从头开始安装了它,版本)。

但是僵局并不是最奇怪的事情。最奇怪的是什么时候出现。

就像我说的那样,该方法的调用看起来像这样:

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
        Configuration.Settings.LoadSettings();

此方法中的所有操作正常之后,因此我导航到下面的第一页:

if (e.PrelaunchActivated == false)
{
    if (rootFrame.Content == null)
    {
        rootFrame.Navigate(typeof(LogScreen), e.Arguments);
    }

    Window.Current.Activate();
}

一切仍然有效。用户需要编写他的代码,我检查该代码是否在设置中可用,然后该用户可以按“确定”进入下一页。在LogScreenViewModel的某个位置,此方法负责:

private void GoForward(bool isValid)
{
    try
    {
        _navigationService.NavigateTo("MainPage"); // it's SimpleIoc navigation from MVVMLight
    }
    catch (Exception e)
    {
        Debug.WriteLine($"ERROR: {e.Message}");
    }
}

当到达_navigationService.NavigateTo(“ MainPage”)时,就会发生死锁。基本上现在,UI线程冻结。如果等待了足够长的时间,我会在Output中看到捕获到的异常,说Messenger似乎已被占用(我无法显示屏幕,因为我现在无法访问该Raspberry),并且在超时后此线程被杀死(例如30秒)-在该用户界面线程解锁后,应用程序进入MainPage。在PC上不会发生这种情况-MainPage立即出现,没有异常,没有死锁。

我尝试在首页上等待大约1分钟,以检查是否会自行触发某些死锁异常,但事实并非如此。只有在尝试进入下一页后,它才会触发。

除了这种一劳永逸的方法之外,我还尝试了什么:

  • 使OnLaunched异步并等待LoadSettings返回Task-同一件事发生在同一位置,在PC上没有问题。
  • 使用: Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => await Configuration.Settings.LoadSettings(); ).AsTask().Wait();如果我没有记错的话,它立即在Wait()上死锁,即使到处都存在ConfigureAwait(false),但这也发生在PC上。
  • 允许LogScreen加载,使其成为OnNavigatedTo方法异步并等待LoadSettings-同一死锁位于同一位置
  • 像第2点一样,允许LogScreen从那里加载并使用Dispatcher。它在PC上到达Wait()后也以相同的方式死锁。
  • 尝试通过用AsTask()。GetAwaiter()。GetResults()替换每次等待来强制LoadSettings完全同步。它在PC上运作良好...当然在Raspberry上也陷入僵局。

我想念什么?我还能尝试什么?因为老实说,在我看来,Win 10 IOT .NET运行时有错误或什么错误。

1 个答案:

答案 0 :(得分:0)

我想我解决了这个问题。这段代码通常说来不是我的,经过一番挖掘之后,我发现我之前有人在导航到MainPage时尝试列出其他一些外部设备。它不是真正的异步安全代码(可能没有人意识到同步上下文),它只能在Win 10上工作,因为在台式机上它正在寻找COM0设备,而我只有COM2,因此引起麻烦的方法甚至没有在全部。

我仍然不知道它与我的配置有多相关(因为某种原因它在没有它的情况下仍然可以工作),但是在我修复了这个旧的非异步安全代码的问题之后,它开始表现出预期的效果。