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

问题描述:

我有一个应用程序,其中两个线程异步写入单个文本框。它的工作原理除了写入文本框的第二个线程覆盖第一个线程刚写入的行外。任何想法或洞察到问题将不胜感激。我正在使用Microsoft Visual C#2008速成版。谢谢。在C#中的异步写入文本框被覆盖

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

你究竟想要我们做什么?这是2线程写入相同的东西时的预期行为...你想要附加文本吗? – 2010-01-11 17:04:06

+2

“SetText”函数附加文本。 – SLaks 2010-01-11 17:06:59

+1

它看起来像你的代码将以书面形式工作。当你发布导致错误的东西时,你可能会编辑一些内容吗? – 2010-01-11 17:10:21

编辑:这可能解决不了问题,但你可能要处理的ProgressChanged事件BackgroundWorkers和设置文本那里。

例如:

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

顺便说一句,你应该重命名SetTextAppendText使代码更清晰。
此外,您可以使用内置代表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(); 
} 
+0

感谢您的建议,但我的接受背景工作者永远不会退出。它被设置为始终运行,因为它需要接收以10ms间隔连续发送的消息。 – 2010-01-11 17:35:03

+0

然后你可以调用'ReportProgress'。 – SLaks 2010-01-11 17:41:17

+1

这是做这件事的正确方法。在我看来,简单地将SetText(或AppendText作为SLaks)的代码体更改为在这里命名它,以便InvokeRequired检查是在里面它应该做的伎俩。 使用ReportProgress是最简单的解决方案,因为它不需要任何InvokeRequired shenanig。 然后,通过通过UI线程引导操作,每次只会在文本框上发生一次写操作。 – 2010-01-11 17:57:15

尝试把锁了,您认为您有并发问题的代码段。可以使用AppendText而不是手动连接字符串。

this.textBox1.AppendText(text); 
+0

虽然它是SetText函数,可能需要锁 – 2010-01-11 17:15:38

+0

哦,不要傻,在线程内部使用实例锁(如果你不注意并且找到恶梦,很容易做到);) – 2010-01-11 17:16:31

+1

SetText函数只能从主线程调用,所以没有什么可以锁定的。他对SetText的调用不会同时发生。 – 2010-01-11 17:17:50

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

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