【问题标题】:Asynchronous Writes To Textbox in C# are Overwritten在 C# 中对文本框的异步写入被覆盖
【发布时间】:2010-01-11 16:59:22
【问题描述】:

我有一个应用程序,其中两个线程异步写入单个文本框。它可以工作,只是第二个线程写入文本框会覆盖第一个线程刚刚写入的行。对问题的任何想法或见解将不胜感激。我正在使用 Microsoft Visual C# 2008 Express Edition。谢谢。

  delegate void SetTextCallback(string text);

  private void SetText(string text)
  {
     this.textBox1.Text += text;
     this.textBox1.Select(textBox1.Text.Length, 0);
     this.textBox1.ScrollToCaret();
  }

  private void backgroundWorkerRx_DoWork(object sender, DoWorkEventArgs e)
  {
     string sText = "";

     // Does some receive work and builds sText

     if (textBox1.InvokeRequired)
     {
        SetTextCallback d = new SetTextCallback(SetText);
        this.Invoke(d, new object[] { sText });
     }
     else
     {
        SetText(sText);
     }
  }

【问题讨论】:

  • 您到底想让我们做什么?这是 2 个线程写入同一事物时的预期行为...您要改为附加文本吗??
  • SetText 函数追加文本。
  • 看起来您的代码将按照编写的方式运行。也许您在发布此内容时编辑了导致错误的内容?

标签: c# delegates multithreading backgroundworker


【解决方案1】:

编辑:这可能无法解决问题,但您可能需要处理 BackgroundWorkers 的 ProgressChanged 事件并将文本设置在那里。

例如:

void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) {
    SetText((string)e.UserState);
}  //Make this method handle the RunWorkerCompleted for both workers

//In DoWork:
    worker.ReportProgress(0, sText);

ProgressChanged 在 UI 线程上触发,因此您无需调用 Invoke

顺便说一句,您可能应该将 SetText 重命名为 AppendText 以使代码更清晰。
此外,您可以使用内置的委托 Action<String>,而不是创建自己的 SetTextCallback 委托类型。

编辑:另外,您应该将InvokeRequired 检查移至SetText

例如:

private void AppendText(string text) {
    if(textBox1.InvokeRequired) {
        textBox1.Invoke(new Action<string>(AppendText), text);
        return;
    }
    this.textBox1.AppendText(text);
    this.textBox1.SelectionStart = textBox1.TextLength;
    this.textBox1.ScrollToCaret();
}

【讨论】:

  • 感谢您的建议,但我的接收后台工作人员从未退出。它设置为始终运行,因为它需要接收以 10 毫秒间隔连续发送的消息。
  • 那你可以拨打ReportProgress
  • 这是正确的做法。在我看来,只需更改 SetText 的代码主体(或 SLaks 在这里命名的 AppendText),以便 InvokeRequired 检查在其中就可以解决问题。到目前为止,使用 ReportProgress 是最简洁的解决方案,因为它不需要任何 InvokeRequired 恶作剧。然后,通过 UI 线程引导操作,一次只会在文本框上发生一次写入。
  • @Zoltan:他已经在 UI 线程上使用 Invoke 调用它。我在第二部分所做的只是移动Invoke 调用。
  • SLaks,它似乎工作得非常好,因为 Textbox1 现在更新得非常快,这是我拍摄的目标之一。但是,这似乎是在 backgroundWorkerRx_DoWork 开始处理数据时抢先控制我的应用程序中的其他组件。这可能是由于添加到文本框中的大量数据造成的吗?
【解决方案2】:

尝试锁定您认为存在并发问题的代码部分。

lock(someObjectUsedOnlyForLocking)
{

}

另外,请尝试使用 AppendText 而不是手动连接字符串。

this.textBox1.AppendText(text);

【讨论】:

  • +1 虽然可能需要锁定的是 SetText 函数
  • 哦,别傻了,在线程内使用实例锁(如果你不注意,很容易找到;)
  • SetText函数只能从主线程调用,所以真的没什么可锁的。他对 SetText 的调用不会同时发生。
【解决方案3】:

我同意 SLaks 的观点,即您应该更正确地使用 BackgroundWorker。但是要“修复”提供的代码,一个问题是调用调用...调用需要调用相同的方法来检查要求,以便将线程与表单的创建者对齐。我通常会做类似于以下的事情(参数用法可能无法编译,但其余的都很好)。老实说,很可能只需要一个处理程序,因为一次只能写入一个线程。

  void backgroundWorkerTx_DoWork(object sender, DoWorkEventArgs e)
  {
     if (this.InvokeRequired)
     {
        this.BeginInvoke(new EventHandler<DoWorkEventArgs>(backgroundWorkerTx_DoWork), sender, e);
        return;
     }
     //The text you wish to set should be supplied through the event arguments
     SetText((string)e.Argument);
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-27
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 2019-04-26
    相关资源
    最近更新 更多