在Ui线程上执行同步操作

时间:2016-03-02 12:45:49

标签: c# xaml asynchronous async-await win-universal-app

我正在尝试开发Windows应用程序并遇到问题。 我有一个MainPage.xaml和另外两个StartScreen.xaml和Player.xaml。 如果某些条件为真,我正在切换MainPage的内容。 所以我在StartScreen中有一个事件,它检查目录是否存在,但每次出错时都会抛出它。

private void GoToPlayer_Click(object sender, RoutedEventArgs e)
    {

        if (Directory.Exists(this.main.workingDir + "/" + IDText.Text + "/Tracks")) // Error occurs here
        {
            this.main.Content = this.main.player; //here i switch between different ui forms
        }
        else
        {
            MessageBox.Text = "CD not found";
            IDText.Text = "";
        }

    }

当它点击其他分支时一切都很好但是当目录可用时我收到以下错误信息:

An exception of type 'System.InvalidOperationException' occurred in System.IO.FileSystem.dll but was not handled in user code

附加信息:不应在UI线程上执行同步操作。考虑在Task.Run中包装此方法。

即使我在if分支中注释代码,错误仍然存​​在。

我试过了:

private async void GoToPlayer_Click(object sender, RoutedEventArgs e)
    {
        await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
            if (Directory.Exists(this.main.workingDir + "/" + IDText.Text + "/Tracks")) // Error occurs here
            {
                this.main.Content = this.main.player; //here i switch between different ui forms
            }
            else
            {
                MessageBox.Text = "CD not found";
                IDText.Text = "";
            }
        });
    }

仍然是同样的错误,因为我理解这应该是异步运行并等到代码完成,但它似乎并非如此。我也试过其他东西,但仍然得到错误。 我不知道如何解决这个问题,有人可以解释为什么会发生这种情况以及如何解决这个问题。

2 个答案:

答案 0 :(得分:4)

如异常所示,您不能在UI线程中同步调用Directory.Exists。将整个代码块放在Dispatcher操作中仍然会在UI线程中调用它。

在UWP应用中,您通常会使用StorageFolder.TryGetItemAsync方法检查文件或文件夹是否存在:

private async void GoToPlayer_Click(object sender, RoutedEventArgs e)
{
    var folder = await StorageFolder.GetFolderFromPathAsync(main.workingDir);

    if ((folder = await folder.TryGetItemAsync(IDText.Text) as StorageFolder) != null &&
        (folder = await folder.TryGetItemAsync("Tracks") as StorageFolder) != null)
    {
        ...
    }
}

请注意,当不允许应用程序访问UnauthorizedAccessException时,您仍可能获得main.workingDir

答案 1 :(得分:4)

[2018年7月22日更新示例]

错误消息告诉您需要知道的一切:

  

考虑在Task.Run中包装此方法

您应该在调用Task.Run时包装代码。这将确保它在后台线程上运行。

这是一个简单的例子:

var picker = new FolderPicker();
picker.SuggestedStartLocation = PickerLocationId.MusicLibrary;
picker.FileTypeFilter.Add(".mp3");
var folder = await picker.PickSingleFolderAsync();
var result = await Task.Run(() => Directory.Exists(Path.Combine(folder.Path, "foobar")));
if (result)
{
  Debug.WriteLine("Yes");
}
else
{
  Debug.WriteLine("No");
}

有关人员关心的背景信息:

根据您的示例代码,我假设您正在阅读音乐库。在这种情况下,.NET文件API通过WinRT API下载,因为它是一个代理位置。由于底层的WinRT API是异步的,因此.NET必须能够为您提供同步行为的假象,并且他们不希望在UI线程上执行此操作。

如果您只处理自己的本地数据文件夹,.NET API将使用底层的Win32 API(已经是同步的),因此可以在没有任何后台线程要求的情况下工作。

请注意,在我的计算机上,这似乎甚至可以在UI线程上运行,因此它可能取决于您要定位的Windows版本。