【问题标题】:Add thread safe method to listBox将线程安全方法添加到 listBox
【发布时间】:2017-07-29 14:13:30
【问题描述】:

我有一个线程,想用进度更新一个列表,但表单控件不是线程安全的。所以我学习了如何用这个article解决。

问题是来源更多,每个来源都有自己的列表来显示进度。那么如何让每个列表框都有自己的ThreadSafeSetText() 方法来清理代码呢?

delegate void SetTextCallback(string text);
private async void btnRun_Click(object sender, EventArgs e)
{
   await Task.Run(() =>
   {
      importSource1();
   });

   //await Task.Run(() =>
   //{
   //   importSource2();
   //});
}

private void importSource1()
{
   // db stuff in a Parallel.For

   SetText("Result");
}

private void SetText(string text)
{
   // InvokeRequired required compares the thread ID of the
   // calling thread to the thread ID of the creating thread.
   // If these threads are different, it returns true.
   if (this.lstImportSource1.InvokeRequired)
   {
      SetTextCallback d = new SetTextCallback(SetText);
      this.Invoke(d, new object[] { text });
   }
   else
   {
      this.lstImportSource1.Items.Insert(0, text);
   }
}

【问题讨论】:

  • 您用“extension-methods”标记了您的问题,因此您可能知道什么是 C# 中的扩展方法以及它们如何帮助您解决这个问题?
  • @Evk 是的,我对此有所了解,但不知道我的实力。我可以检查示例并添加方法。但不知道如何在里面分配一个委托?也不确定是否符合 ThreadSafe 的条件。
  • 你不应该像这样打电话给Invoke。在 await 之后更新 UI,当您在 UI 线程中时,而不是从您在另一个线程中运行的操作中。
  • @Servy 你的意思是我把"strResult"保存在一个全局变量中,并在线程结束时设置listBox?
  • @JuanCarlosOropeza 不,你应该从方法中返回它,而不是依赖全局状态。

标签: c# thread-safety extension-methods


【解决方案1】:

基于ImportSource 是一种执行数据库过程的方法的评论,我的建议使用async-await 方法。
Async-await 旨在在您的代码执行“触及”外部操作时更有效地使用资源资源(数据库、文件系统、Web API 等)。

因为在执行数据库查询期间(例如)您的方法“什么都不做”,它只发送请求并等待响应。创建“什么都不做”被视为浪费资源的新线程。

async-await 操作将在一个线程(大部分时间)上执行,您可以在其中简单地使用 UI 控件。

您可以为每个过程创建自己的异步方法

public async Task ExecuteProcedure1Async()
{
    using (var connection = new SqlConnection("connection string"))
    using (var command = new SqlCommand("dbo.sqlProcedure1", connection))
    {
        command.CommandType = CommandType.StoredProcedure;

        // Here execution will send request to the database
        // and be returned to the caller of this method         
        await connection.OpenAsync();
        // Continue after OpenAsync is completes and
        // Here execution will again will be returned to the caller 
        await command.ExecuteCommandAsync();
        // Continues after ExecuteCommandAsync is completed
    }      
}

可以为另一个存储过程创建类似的方法

以下所有程序将“几乎”同时执行。
因为我们会发送请求而不等待响应 并且仅在所有程序完成后等待

private async Task ImportSource1Async()
{
    // Because asynchronoud method returns Task
    // We can create collection of tasks and "await" them all
    // after they have been started
    var tasks = new[] 
    {
        ExecuteProcedure1Async(),
        ExecuteProcedure2Async(),
        ExecuteProcedure3Async()
    };

    await Task.WhenAll(tasks);
}

然后您可以在button_click 事件处理程序中将所有ImportSourceAsync 方法组合在一起并使用UI 控件。

private async void btnRun_Click(object sender, EventArgs e)
{
    await ImportSource1Async();
    lstImportSource1.Items.Insert(0, "Import source 1 complete");

    await ImportSource2Async();
    lstImportSource1.Items.Insert(0, "Import source 2 complete");

    await ImportSource3Async();
    lstImportSource1.Items.Insert(0, "Import source 3 complete");
}

【讨论】:

  • 我看到的问题是结果文本需要过程中的东西,比如startTime, rowsProcess, totalTime,此外,如果出现错误,我有一个不同的lstErrror 来记录发生的事情。但是看看你的方法,我应该把所有这些都包装在一个类中,然后从 ImportSource 函数中返回它?
  • 您可以更改ImportSource1Async 以返回包含导入过程结果的数据。以“肮脏”的方式,您可以更新 ImportSourceAsync 内的 UI 控件 - 在这种情况下,整个执行将在 UI 线程上进行
猜你喜欢
  • 2012-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多