【问题标题】:Win 10 IOT thread deadlocking in UWPUWP 中 Win 10 IOT 线程死锁
【发布时间】:2019-04-26 09:14:52
【问题描述】:

我想做什么: - 在第一页加载之前从 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 线程冻结。如果我等待足够长的时间,我会在输出中看到捕获的异常,说信使似乎被占用(我无法显示屏幕,因为我现在无权访问那个树莓)并且在一些超时后这个线程被杀死(比如 30秒或其他东西) - 在该 UI 线程解锁并且应用程序继续到 MainPage 之后。它不会在 PC 上发生 - MainPage 立即出现,没有异常,没有死锁。

我尝试在第一页等待大约 1 分钟,以检查是否会自行触发某些死锁异常 - 但它不会。只有在我尝试进入下一页后才会触发。

我还尝试过什么来代替这种即发即弃的方法:

  • 使 OnLaunched 异步并等待 LoadSettings 返回 Task - 同样的事情发生在同一个地方,在 PC 上没有问题。
  • 使用: Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =&gt; await Configuration.Settings.LoadSettings(); ).AsTask().Wait(); 如果我没记错的话,它在 Wait() 上立即死锁,即使到处都有 ConfigureAwait(false),但它也发生在 PC 上。
  • 允许 LogScreen 加载,使其 OnNavigatedTo 方法异步并等待 LoadSettings - 相同的死锁在同一个地方
  • 允许 LogScreen 从那里加载和使用 Dispatcher,就像在第 2 点中一样。它在到达 Wait() 后也以同样的方式死锁,在 PC 上也是如此。
  • 试图通过将每个等待替换为 AsTask().GetAwaiter().GetResults() 来强制 LoadSettings 完全同步。它在 PC 上运行良好……当然在 Raspberry 上也出现了死锁。

我错过了什么?我还能尝试什么?因为老实说,在我看来,Win 10 IOT .NET 运行时存在错误或其他问题。

【问题讨论】:

  • 您是否尝试在调试器死锁时暂停它以查看卡在哪里?
  • 就像我说的:_navigationService.NavigateTo("MainPage");直到这个死锁的线程在超时后被杀死,它才会进入 MainPage。在那之前它一直在那里。仅在覆盆子上。如果我根本不加载设置它不会被阻塞,所以它肯定与这些异步操作有关。
  • 我的意思是实际的调用堆栈。不是你叫什么去那里。
  • 我想我试图检查调用堆栈,但它是空的。但我不确定这是否是这个特殊问题。周末后我可以检查一下。
  • @Khaine,我没有重现这个问题。你能在共享仓库中提供一个简单的项目吗?

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


【解决方案1】:

我想我解决了这个问题。这段代码一般来说不是我的,经过一番挖掘后,我注意到在我之前有人试图在导航到 MainPage 时列出一些其他外部设备。它不是真正的异步安全代码(有人可能不知道同步上下文),它只能在 Win 10 上运行,因为在桌面上它正在寻找 COM0 设备而我只有 COM2,所以导致问题的方法甚至没有被调用全部。

我仍然不知道它与我的配置有多相关(因为它在没有它的情况下也能正常工作),但在我修复了这个旧的非异步安全代码的问题后,它开始按预期运行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-18
    • 2018-05-22
    • 2020-01-10
    • 2010-09-10
    • 2018-06-16
    • 2016-04-08
    • 1970-01-01
    相关资源
    最近更新 更多