【发布时间】: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 () => 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