【问题标题】:How to do C# asynchronous programming?如何进行 C# 异步编程?
【发布时间】:2015-01-29 11:58:13
【问题描述】:

我正在使用很久以前用 Visual Basic.net 编写的数据库中的导出工具。

我切换到 C# 并喜欢用 C# 重新编程,因为我积累了比以前更多的经验 :-)

我的 C# 应用程序的 UI 挂起,因为有一个大型数据库查询要做。所以我已经了解了异步编程。也尝试过线程和任务,但我无法找到解决我问题的正确方法。

这就是我所拥有的:它是一个 Windows 窗体应用程序,并且有一个创建新线程的开始按钮。

名为 Export 的方法是我在名为 actions.cs 的第二个类文件中创建的非静态方法。它是非静态的,因为该方法应该在代码中经常重复使用,但参数不同。

所以我在Form1上实例化Button_Clicked事件中的方法,并带有相应的参数:

actions KuliMon = new actions()
        {
            ExportPath = settings.ReadActSetting("baexport", "ExportPfad", ""),
            Trennzeichen = settings.ReadGlobSetting("Trennzeichen", ";"),
            ConnectionString = settings.ReadGlobSetting("Mand1_odbc", ""),
            SQLFile = "kuli.sql",
            ExportAktion = "kuli"
        };

然后我从 Button_click 事件启动线程,如下所示:

Thread ExportThread = new Thread(KuliMon.Export);
        ExportThread.Start();

这行得通。没有粘贴 GUI。但是现在问题来了。我在 actions.cs 中的方法 Export 是将 DB-Query 导出到 csv-File 中,但也应该以字符串变量的形式返回结果,然后我可以将其显示在 Form1 上的 TextBox 中。

通过阅读我发现 Invoke-Method 这一点帮助很大。在 Thread.Start() 下,我添加了以下内容:

this.Invoke((MethodInvoker)delegate
        {
             tx_main_t1.Text = "Hello";
        });

当我点击按钮时,TextBox 会说“你好”。但不是你好,我需要在线程中运行的方法导出的返回字符串。这里的问题是如何获取带有查询结果的字符串。

在我的理解中,线程方法必须调用一个 void 方法并且不能返回一个值。

我想创建一个公共属性字符串并在 Export 中使用返回值填充字符串,如下所示:

public string results { get; set; }

我尝试过在方法导出中使用 return ReturnValue 而不是

results = ReturnValue;

然后,在 Form1 中,我尝试用 KuliMon.results 填充 TextBox,但它是空的,因为我按照我的想法创建了一个 Export 实例。

【问题讨论】:

  • 您是否仅限于使用 .Net 3.5 或 4.0?或者你可以使用.Net4.5吗?在 .Net4.5 中有名为 Async 和 Await 的新关键字,它们有助于创建反应式 UI。好吧,您可以通过使用 AsyncBridge 库在 3.5 或 4.0 中使用相同的功能
  • 您使用的是哪个 .NET 框架版本?另外,这是什么数据库? SQL Server?
  • 创建一个带有字符串属性的对象,这个对象是你的窗口知道的。现在将这些对象提供给您的线程。做你的工作。将 Result 写入您的字符串变量并让您的线程抛出一个事件(由您的 UI 处理)。在这个处理程序中,您只需读取变量并显示它的内容。

标签: c# multithreading asynchronous task


【解决方案1】:

数据库查询和写入文件都是 I/O 绑定操作,因此它们非常适合 asyncawait

首先,您将定义 KuliMon.Export 的异步版本:

async Task<string> ExportAsync()
{
  var data = await myQuery.ToListAsync();

  await myStream.WriteAsync(...);

  return results;
}

例如,您可以使用Entity Framework 6 for asynchronous query support

然后你可以从你的 UI 中调用它:

async void button1_Clicked(...)
{
  TextBox1.Text = await myInstance.ExportAsync();
}

如果您出于某种原因不能使用异步数据库查询(例如,我认为 Oracle 目前不支持它们),那么您可以使用调用同步 API 的后台线程。请注意,Task.RunThreadBackgroundWorker 的现代替代品:

string Export();
...
async void button1_Clicked(...)
{
  TextBox1.Text = await Task.Run(() => myInstance.Export());
}

【讨论】:

  • 啊,好的,所以我将专注于创建一个任务。虽然我不得不说这对我来说真的很难。但我会尝试直到它起作用。好吧,我要感谢所有回复者的帮助和善意。
【解决方案2】:

您应该查看 BackGroundWorker 类
https://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx

您可以分配一个函数在工作人员完成工作时调用,并让该函数更新您的 UI。

【讨论】:

    【解决方案3】:

    使用 .Net 4.5 和 c#5 中支持的 async await(您可以在早期的 .Net 版本中获得对 3.54.0 的 AsyncBridge 包的支持)

    private async void button1_Click(object sender, EventArgs e)
    {
         button1.Enabled = false;
         try
         {
              //Code will wait here for your method to return without blocking UI Exceptions or Result will be automatically Scheduled to UI thread
              string result = await DoStuffAsync("myParameter");
         }
         catch
         {
              MessageBox.Show("Ups an error");
         }
         finally
         {
             button1.Enabled = true;
         }
    
    }
    
    /// <summary>
    /// Wraps the synchron implementation off DoStuff
    /// </summary>    
    public Task<string> DoStuffAsync(string s)
    {
       return Task.Factory.StartNew(DoStuff, s); //here parameter s is forwarded to your synchronus implementation
    }
    
    /// <summary>
    /// Your orginal synchron implementation with the dbQuerry
    /// or long running calculations
    /// </summary>   
    public string DoStuff(string s)
    {
          //do your normal code;
          return result
    }
    

    【讨论】:

    • 您好 BoeseB,当我使用任务时,我必须使用静态方法 - 对吧?我认为这不是最好的方法,因为我想为我的方法 Export 提供一些参数。
    • 不,没有什么可以强迫您使用静态方法。
    • 好的,因为我正在使用 c# 5.0 进行编程,而且我在异步编程方面还很陌生,这对于未来的方向来说是更好的方式吗?使用 Tasks 还是 backgroundworker?
    • Backgroundworker 是在 windows 窗体中使用线程的旧概念。 async await 是新人,在我看来比 backgroundworker 更容易使用。
    猜你喜欢
    • 1970-01-01
    • 2011-01-19
    • 1970-01-01
    • 2016-12-04
    • 1970-01-01
    • 1970-01-01
    • 2018-03-08
    • 1970-01-01
    • 2014-04-29
    相关资源
    最近更新 更多