对C#中文本框的异步写入被覆盖

对C#中文本框的异步写入被覆盖,c#,delegates,multithreading,backgroundworker,C#,Delegates,Multithreading,Backgroundworker,我有一个应用程序,其中两个线程异步写入单个文本框。它可以工作,除了写入文本框的第二个线程覆盖第一个线程刚刚写入的行。对这个问题的任何想法或见解都将不胜感激。我正在使用Microsoft Visual C#2008快速版。谢谢 delegate void SetTextCallback(string text); private void SetText(string text) { this.textBox1.Text += text; this.textBox

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

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

EDIT:这可能无法解决问题,但您可能希望处理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);
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();
}
ProgressChanged在UI线程上启动,因此您不需要调用
Invoke

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

编辑:此外,您可能应该将
invokererequired
检查移动到
SetText

例如:

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);
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();
}
private void AppendText(字符串文本){
if(textBox1.invokererequired){
textBox1.Invoke(新操作(AppendText),text);
返回;
}
this.textBox1.AppendText(text);
this.textBox1.SelectionStart=textBox1.TextLength;
this.textBox1.ScrollToCaret();
}

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

lock(someObjectUsedOnlyForLocking)
{

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

this.textBox1.AppendText(text);

我同意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);
  }
void backgroundWorkerTx\u DoWork(对象发送方,DoWorkEventArgs e)
{
if(this.invokererequired)
{
this.BeginInvoke(新事件处理程序(backgroundWorkerTx_DoWork),发送方,e);
返回;
}
//要设置的文本应通过事件参数提供
SetText((字符串)e.参数);
}

您希望我们做什么?这是两个线程写入同一内容时的预期行为…是否要追加文本?
SetText
函数追加文本。看起来您的代码将按编写的方式工作。可能是您在发布此消息时编辑掉的某个内容导致了错误?+1尽管可能需要锁的是SetText函数哦,而且不要傻了,在线程中使用实例锁(如果您不注意,很容易找到一个噩梦;))SetText函数只能从主线程调用,因此实际上没有什么可锁定的。他打给SetText的电话不是同时发生的。谢谢你的建议,但我的receive backgroundworker从不退出。它设置为始终运行,因为它需要接收以10毫秒间隔连续发送的消息。然后您可以调用
ReportProgress
。这是正确的方法。在我看来,简单地更改SetText(或SLaks在这里将其命名为AppendText)的代码体,使invokererequired检查位于其内部就可以做到这一点。使用ReportProgress是迄今为止最整洁的解决方案,因为它不需要任何调用所需的恶作剧。然后,通过UI线程引导操作,每次文本框上只会发生一次写入。@Zoltan:他已经在UI线程上使用
Invoke
调用了它。我在第二部分所做的就是移动
Invoke
call.SLaks,它似乎在Textbox1中运行得非常好,现在更新得非常快,这是我拍摄的内容之一。然而,这似乎是在BackgroundWorkerRrx_DoWork开始处理数据时,就抢占了对应用程序中其他组件的控制权。这可能是由于添加到文本框中的大量数据造成的吗?