Task.Run(()=> Method());不运行方法?

时间:2016-04-27 11:37:46

标签: c# wpf visual-studio task

在我的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
    }
  }
}

无论我尝试用Authenticate拨打Tasks哪个方式,它都无法运行。我使用Task错了吗?

使用await(和async)会导致抛出异常:

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

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

使用Authenticate()调用该方法可以毫无问题地运行整个方法,但它会冻结UI,使“AuthText.Visibility = Visibility.Visible;”无用的。

老实说,我真的只想让UI更新消息“Authenticating ...”,然后单击按钮时运行方法中的所有内容。是否有更简单的方法可以做到这一点?

这是参考的工作代码:

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;
        });
      }
    }
  }

2 个答案:

答案 0 :(得分:2)

问题在于你并没有等待异步任务完成,所以看起来像#34;没有任何反应&#34; - 事实上确实发生了一些事情。当您调用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关键字是真正发生所有魔法的地方......但它会按照您的预期运作,即; &#34;东西发生了#34;。

当我确实击败Stephen Cleary时,我通常会将人们指向他的博客,this one尤其应该帮助您澄清这一点。

注意

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

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

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

<强>更新

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

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
}

答案 1 :(得分:0)

在新线程中修改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控件。