【问题标题】:C# async/await write to textboxC#异步/等待写入文本框
【发布时间】:2017-08-10 15:56:36
【问题描述】:

不用说我是 C# 新手,并且在此过程中会做一些基本的事情来学习。我正在尝试了解异步/等待功能。我有一个带有 2 个文本框和一个按钮的表单。当我单击该按钮时,它会检查远程计算机的服务状态(在这种情况下是远程注册表。在检查时,它会写入它正在检查注册表并告诉用户等待。然后如果状态停止则启动并获取一些值我查找并将它们写入第二个文本框。

我的代码有效,并且我能够获得我的价值观。但 GUI 在此期间冻结。我试图实现 backgroundworker、thread 和 Async/Wait,但没有成功。我试着做任务,但无法让它发挥作用。

这里或其他网站上显示的所有示例都简单地返回 int,而我试图返回一个将状态和值写入文本框的字符串。我试图将其转换为字符串但没有成功。

长话短说,对于这种类型的流程,有什么更好的方法?有人可以告诉我如何并评论一路上做什么,以便我更好地理解吗?我想学习如何做到这一点,但同时要了解原因并学习编写更简洁的代码。

感谢大家提前抽出时间。

string ComputerName = "Lab01";
    public frmRegChecker()
    {
        InitializeComponent();
    }


    private void Button1_Click(object sender, EventArgs e)
    {
      Check_Status();        

    }

    private void Check_Status()
    {
        TxtBoxstatus.AppendText("Checking Remote Registry service status on computer : " + ComputerName);
        TxtBoxstatus.AppendText(Environment.NewLine);
        TxtBoxstatus.AppendText("Please wait... ");

         ServiceController sc = new ServiceController("RemoteRegistry", ComputerName);
            try
            {


                TxtBoxstatus.AppendText("The Remote Registry service status is currently set to : " + sc.Status.ToString());
                TxtBoxstatus.AppendText(Environment.NewLine);


                if (sc.Status == ServiceControllerStatus.Stopped)
                {

                    // Start the service if the current status is stopped.

                    TxtBoxstatus.AppendText("Starting Remote Registry service...");
                    TxtBoxstatus.AppendText(Environment.NewLine);
                    try
                   {
                       // Start the service, and wait until its status is "Running".
                        sc.Start();
                        sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 3));
                        sc.Refresh();
                        sc.WaitForStatus(ServiceControllerStatus.Running);

                        // Display the current service status.
                        TxtBoxstatus.AppendText("The Remote Registry status is now set to:" + sc.Status.ToString());
                       richTextBox1.AppendText(Environment.NewLine);
                        try
                        {
                           var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
                            var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
                            string _OSVersion = (key.GetValue("CurrentVersion")).ToString();
                           richTextBox1.AppendText("OS version is : " + _OSVersion);
                            richTextBox1.AppendText(Environment.NewLine);
                       }
                        catch (InvalidOperationException)
                       {

                            richTextBox1.AppendText("Error getting registry value from" + ComputerName);
                            richTextBox1.AppendText(Environment.NewLine);
                        }
                    }
                    catch (InvalidOperationException)
                    {

                        richTextBox1.AppendText("Could not start the Remote Registry service.");
                        richTextBox1.AppendText(Environment.NewLine);
                    }
                }
                else if (sc.Status == ServiceControllerStatus.Running)
               {
                   try
                   {
                     var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
                     var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
                            string _OSVersion = (key.GetValue("CurrentVersion")).ToString();
                     richTextBox1.AppendText("OS version is : " + _OSVersion);
                     richTextBox1.AppendText(Environment.NewLine);
                    }
                    catch (InvalidOperationException)
                    {

                            richTextBox1.AppendText("Error getting registry value from" + ComputerName);
                            richTextBox1.AppendText(Environment.NewLine);
                    }

                }
           }

          }
          catch
           {                  
             richTextBox1.AppendText("Error getting registry value from " + ComputerName);
             richTextBox1.AppendText(Environment.NewLine);

           }
}

【问题讨论】:

  • 后台工作人员应该为此工作,而这正是他们设计的工作。后台工作人员遇到了什么问题?
  • 您好,埃里克,感谢您的快速回复。我在 backgroundworker 中看到的示例都带有进度条,它向标签结果报告整数。我只是无法实现它如何将我的状态报告以字符串形式返回到文本框。我收到错误,例如您无法从其他控件类型的内容访问文本框。
  • 您无法使async 解决方案正常工作,但您根本没有显示任何异步代码。
  • 您好 Servy,我不确定要使用哪种解决方案以及为什么。这就是为什么我先问这个的原因。有太多方法可以做我想做的事情,我正在努力学习最干净但最好的方法。
  • @Besiktas 你说你写了一个异步解决方案,但是它没有工作,你还没有展示一个异步解决方案。展示你的异步解决方案,并解释为什么它不起作用。

标签: c# multithreading async-await backgroundworker


【解决方案1】:

不幸的是,ServiceController 是一个相当过时的类,本身并不支持async/await。如果可能的话,这将是最干净的解决方案。

因此,您可以将async/awaitTask.Run 一起使用。 Task.Run 在后台线程上执行代码,您可以在 UI 中使用 async/await 来使用该后台操作。这种方法允许以自然的方式传播和处理异常,并且它还允许自然地处理返回值。为简单起见,暂时去掉文本框更新,第一步如下所示:

private async void Button1_Click(object sender, EventArgs e)
{
  try
  {
    var osVersion = await Task.Run(() => CheckStatus());
    richTextBox1.AppendText("OS version is : " + osVersion);
    richTextBox1.AppendText(Environment.NewLine);
  }
  catch (InvalidOperationException ex)
  {
    richTextBox1.AppendText("Error getting registry value from" + ComputerName);
    richTextBox1.AppendText(ex.ToString());
    richTextBox1.AppendText(Environment.NewLine);
  }
}

private string CheckStatus()
{
  ServiceController sc = new ServiceController("RemoteRegistry", ComputerName);
  if (sc.Status == ServiceControllerStatus.Stopped)
  {
    // Start the service if the current status is stopped.
    // Start the service, and wait until its status is "Running".
    sc.Start();
    sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 3));
    sc.Refresh();
    sc.WaitForStatus(ServiceControllerStatus.Running);
  }

  var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
  var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
  return (key.GetValue("CurrentVersion")).ToString();
}

接下来,添加进度更新。有一个内置机制 - IProgress<T>/Progress<T>,它的工作原理如下:

private async void Button1_Click(object sender, EventArgs e)
{
  try
  {
    var progress = new Progress<string>(update =>
    {
      TxtBoxstatus.AppendText(update);
      TxtBoxstatus.AppendText(Environment.NewLine);
    });
    var osVersion = await Task.Run(() => CheckStatus(progress));
    richTextBox1.AppendText("OS version is : " + osVersion);
    richTextBox1.AppendText(Environment.NewLine);
  }
  catch (InvalidOperationException ex)
  {
    richTextBox1.AppendText("Error getting registry value from" + ComputerName);
    richTextBox1.AppendText(ex.ToString());
    richTextBox1.AppendText(Environment.NewLine);
  }
}

private string CheckStatus(IProgres<string> progress)
{
  progress?.Report("Checking Remote Registry service status on computer : " + ComputerName);
  progress?.Report("Please wait... ");

  ServiceController sc = new ServiceController("RemoteRegistry", ComputerName);
  progress?.Report("The Remote Registry service status is currently set to : " + sc.Status.ToString());
  if (sc.Status == ServiceControllerStatus.Stopped)
  {
    // Start the service if the current status is stopped.
    progress?.Report("Starting Remote Registry service...");
    // Start the service, and wait until its status is "Running".
    sc.Start();
    sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 3));
    sc.Refresh();
    sc.WaitForStatus(ServiceControllerStatus.Running);
    progress?.Report("The Remote Registry status is now set to:" + sc.Status.ToString());
  }

  var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
  var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
  return (key.GetValue("CurrentVersion")).ToString();
}

注意关注点分离:唯一涉及 UI 对象的代码是 UI 事件处理程序。包含实际程序逻辑的CheckStatus 方法与 UI 分离 - 它只知道它可以报告进度字符串并返回字符串结果。

【讨论】:

  • 非常感谢斯蒂芬。所以在线var osVersion = await Task.Run(() =&gt; CheckStatus()); 我给出什么论据?是否有更快或更好的 servicecontroller 的“更新”版本?还有“进步”吗?意思是“如果进步”再次感谢。这真的解释了很多。吓到我多少我不知道。
  • 您将进度传递给它。 progress?.Report(..);if (progress != null) progress.Report(..); 的简写
  • 再次感谢。是的,我添加了它并且它有效。 :) 非常感谢。
【解决方案2】:

这可以使用 async/await 轻松完成。一个例子:

一个简单的 WPF 表单:

<Window x:Class="WpfApp1.MainWindow"
    ...>
  <Grid>
    <Button Name="button" Content="Start" Click="Button_Click"/>
    <TextBox Name="textButton" />
  </Grid>
</Window>

以及相关的代码隐藏:

public partial class MainWindow:Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private async void Button_Click(object sender, RoutedEventArgs e)
    {                        
           textButton.Text = "Running...";
           string result = await DoIt();
           textButton.Text = result;
    }

    private async Task<string> DoIt()
    {
        await Task.Delay(3000);
        return "Finished...";
    }
}

单击按钮时,“DoIt”中长时间运行的“计算”会异步启动。在运行时,UI 保持响应。 当 DoIt 返回它的结果时(在这个例子中是在 3 秒的虚拟延迟之后),'click' 事件处理程序继续......

备注:

  • 为简单起见,此示例使用代码隐藏。使用 MVVM 模式(异步操作从命令启动)也可以使用该技术。

  • 'async void' 是一种反模式,但用于遵守代码中自动生成的事件处理程序。

  • 在现实世界的应用程序中,异步“DoIt”方法可能位于后端“模型类”中。

  • 该示例假定您只想在长时间运行的操作完成后更新 UI。如果你想要中间更新,有几个选项(不幸的是有点复杂)。

【讨论】:

    【解决方案3】:

    我认为您仍然可以使用 backgroundworker 让您的文本框显示消息而不冻结 GUI。从这个问题Here 中参考如何从后台工作人员返回一个对象。

    【讨论】:

    • BGW 用于长时间运行的 CPU 绑定工作,而不是 IO 绑定工作。此外,没有真正的理由在基于 TPL 的解决方案上使用它,这正是 OP 所说的他们正在寻找的。​​span>
    猜你喜欢
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    • 1970-01-01
    • 2018-09-30
    • 1970-01-01
    • 1970-01-01
    • 2018-09-26
    • 1970-01-01
    相关资源
    最近更新 更多