在执行任务时解冻 UI

Unfreeze UI whilst performing a task

这里是线程新手。

我正在尝试制作一个从 osu 中提取用户个人资料数据的程序!网站,同时应用程序冻结。您不能在 window 或任何东西周围拖动。我试过做一个任务 returns 等待后的结果,但它仍然冻结

private async Task<OsuUserBest[]> GetUserScores()
{

    return await Task.FromResult(osu.GetUserBest(userIdText.Text, 0, 100));
}

private async void OnGoClick(object sender, RoutedEventArgs e)
{
    try
    {
        userScores = await GetUserScores();
        //...

我也试过 Task.Run( () => {code}) 但它告诉我另一个线程拥有它。我不能直接等待 osu.GetUserBest(userIdText.Text, 0, 100) 因为它不包含 GetAwaiter.

的定义

同样,我对线程和异步任务非常陌生,所以请原谅我的白痴 =)

如果您希望我提供任何其他帮助,请随时提出。

GitHub repository

您不能从 UI 线程(调度程序线程)以外的线程访问 UI 元素(或任何 DispatcherObject 一般)。使用 Dispatcher.InvokeAsync 访问这些对象或在 运行 后台操作之前访问它们(复制它们的值):

private async Task<OsuUserBest[]> GetUserScoresAsync()
{
  // Access the DispatcherObject on the UI thread
  string userId = userIdText.Text;

  return await Task.Run(() => osu.GetUserBest(userId, 0, 100));
}

将网络请求等 IO 操作包装到 Task.Run 中应该是绝对的例外。现代 API 将始终公开异步操作。

您并不真的需要 GetUserScores 方法。您可以只使用 Task.Run method directly inside your asynchronous Click even handler. This way you'll also avoid violating Microsoft's recommendation 关于不为同步方法公开异步包装器的内容。

您必须知道 Task.Run 方法会在 ThreadPool 线程上调用提供的 lambda,因此无论您卸载什么代码,都不应与 UI 线程有关联。这实际上意味着您不应该触摸(甚至嗅探)卸载代码中的任何 UI 元素。与 UI 组件的交互应该只在 UI 线程上完成。

private async void OnGoClick(object sender, RoutedEventArgs e)
{
    try
    {
        string userId = userIdText.Text;
        OsuUserBest[] userScores = await Task.Run(() => osu.GetUserBest(userId, 0, 100));
        //...
    }
    catch (Exception ex)
    {
        //...
    }
}