【问题标题】:Task.Run(() => Method()); doesn't run the method?Task.Run(() => 方法());不运行该方法?
【发布时间】:2016-04-28 07:58:41
【问题描述】:

在我的 WPF 应用程序中:

using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using System.Diagnostics;

namespace CloudKey
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
    public Page1()
    {
        InitializeComponent();

        AuthText.Visibility = Visibility.Hidden;

    }
    private async void button_Click(object sender, RoutedEventArgs e)
    {

        AuthText.Visibility = Visibility.Visible;
        await Task.Run(() => Authenticate());
        Task.Factory.StartNew(() => Authenticate());
        Task.Run(() => Authenticate());
        Authenticate();
    }
    void Authenticate()
    {
        //Do Stuff
    }
  }
}

无论我尝试用Tasks 以哪种方式调用Authenticate,它都无法运行。我是不是用错了Task

使用 await(和 async)会引发异常:

System.InvalidOperationException 未处理 消息:在 mscorlib.dll 中发生“System.InvalidOperationException”类型的未处理异常 附加信息:调用线程无法访问此对象,因为不同的线程拥有它。

仅使用 Task.Run 或 Task.Factory.StartNew 会导致 Authenticate 方法根本不运行。如果我向 Authenticate 方法添加断点,则无法到达。

只需使用 Authenticate() 调用该方法即可运行整个方法而不会出现问题,但它会冻结 UI 并生成“AuthText.Visibility = Visibility.Visible;”没用。

说实话,我真的只是希望 UI 更新为消息“正在验证...”,然后在我单击按钮时运行该方法中的所有内容。是否有更简单的方法来做到这一点?

这是供参考的工作代码:

using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using System.Diagnostics;
using System.Threading.Tasks;

namespace CloudKey
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
    public Page1()
    {
        InitializeComponent();

        //private void PasswordBox_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { button_Click } }
        AuthText.Visibility = Visibility.Hidden;

    }
    private void button_Click(object sender, RoutedEventArgs e) //ON CONTINUE BUTTON CLICK
    {
        AuthText.Visibility = Visibility.Visible;
        Task.Run(() => Authenticate());
    }
    void Authenticate()
    {
        Dispatcher.Invoke(
        () =>
        {
          //ALL MY CODE HERE;
        });
      }
    }
  }

【问题讨论】:

  • Task.Factory.StartNew(() => Authenticate());应该可以正常工作 - 尽管调用其中的任何 UI 内容都会导致奇怪的事情发生
  • Authenticate 是做什么的?可能是在被吞噬的非 UI 线程上抛出异常?
  • 你怎么知道什么都没发生?定义什么都没有发生?如果你断点,它到达的方法是什么?
  • Task.Run 会像 StartNew 一样启动一个执行该任务的新线程,因为这是一个新线程,您不能保证它在您所依赖的检查之前或之后运行,请尝试添加 await 键工作,这将指示您的代码在继续之前等待任务完成
  • 他的代码可能在完成之前就停止了,我也遇到过这种情况,感觉很愚蠢。 @MikeT

标签: c# wpf visual-studio task


【解决方案1】:

问题在于您没有等待异步任务完成,因此看起来像“什么都没有发生” - 实际上确实发生了一些事情。当您调用Task.RunTask.Factory.StartNew 时,您实际上是在执行即发即弃,除非您正确处理Task

private async void button_Click(object sender, RoutedEventArgs e)
{
    await Task.Run(() => Authenticate()); // Stuff happens
}

void Authenticate()
{
    // DO STUFF
}

在上面的示例中,将关键字async 添加到事件处理程序允许该方法使用await 关键字。 await 关键字是所有魔法真正发生的地方……但它会按您的预期工作,即; “事情发生了”。

当我在这些答案上击败 Stephen Cleary 时,我通常会将人们引向他的博客,this one 尤其应该有助于为您澄清这一点。

注意

强烈建议不要写async void!唯一的例外是在您将其应用于事件处理程序的示例中。最后,当使用 TaskTask&lt;T&gt;async / await 关键字时 - 在整个堆栈中这样做。例如,我会更改您的 Authenticate 方法以返回 Task,以便可以等待它。尝试在尽可能低的级别调用Task.Run

private async void button_Click(object sender, RoutedEventArgs e)
{
    await Authenticate(); // Stuff happens
}

Task Authenticate()
{
    return _authModule.Authenticate();
}

更新

根据您的 cmets,执行以下操作:

private void button_Click(object sender, RoutedEventArgs e)
{
    bool authenticated = false;
    try
    {
        AuthText = "Authenticating...";
        authenticated = Authenticate(); // Stuff happens
    }
    finally
    {
        AuthText = authenticated ? "Authenticated" : "Oops!";
    }
}

bool Authenticate()
{
    // Return if auth was successful
}

【讨论】:

  • 在更改我的代码以反映您的代码后,我收到一个错误:System.InvalidOperationException 未处理消息:mscorlib.dll 中发生“System.InvalidOperationException”类型的未处理异常附加信息:调用线程无法访问这个对象,因为另一个线程拥有它。
  • 在原帖中添加了更多信息。
  • 如果您有任何正在与之交互的 UI 元素,则需要编组回 UI 线程。
  • 说实话,我真的只是希望 UI 更新为消息“正在验证...”,然后在我单击按钮时运行该方法中的所有内容。有没有更简单的方法来做到这一点?
  • 嘿,adeel41 提供的答案最终奏效了。感谢您的所有帮助,我真的很感激!我会支持你,但我只需要 14 分 / 15 分。 xD
【解决方案2】:

当您在新线程中修改 UI 内容时,您需要使用 Dispatcher.Invoke 或者您可以使用 InvokeAsync

    private void Button_Click( object sender, RoutedEventArgs e ) { Task.Run( () => Authenticate() ); }

    public void Authenticate()
    {
        Dispatcher.Invoke(
            () =>
            {
                ClickButton.Content = "Text Changed";
            } );
    }

通过使用Dispatcher,您告诉 WPF 在有权更新您的 GUI 控件的主线程上运行此代码块。

【讨论】:

  • 哇,非常感谢!我这样做了: private void Button_Click( object sender, RoutedEventArgs e ) { Task.Run( () => Authenticate() ); } public void Authenticate() { Dispatcher.Invoke( () => { 我的整个代码在这里; } ); },它的工作原理!
  • 嘿 Adeel,我想在后台运行一些代码时运行一个新动画。如果您愿意,问题位于HERE
猜你喜欢
  • 2020-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 2019-03-28
相关资源
最近更新 更多