Асинхронные записи в текстовое поле в C # перезаписываются - PullRequest
2 голосов
/ 11 января 2010

У меня есть приложение, в котором два потока пишут асинхронно в одно текстовое поле. Это работает, за исключением того, что второй поток для записи в текстовое поле переписывает строку, которую только что написал первый поток. Любые мысли или понимание проблемы будет принята с благодарностью. Я использую 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);
     }
  }

Ответы [ 3 ]

3 голосов
/ 11 января 2010

РЕДАКТИРОВАТЬ : Это может не решить проблему, но вы можете обработать событие 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 запускается в потоке пользовательского интерфейса, поэтому вам не нужно вызывать 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();
}
1 голос
/ 11 января 2010

Попробуйте установить блокировку раздела кода, с которым, по вашему мнению, возникают проблемы с параллелизмом.

lock(someObjectUsedOnlyForLocking)
{

}

Кроме того, попробуйте использовать AppendText вместо ручного объединения строк.

this.textBox1.AppendText(text);
0 голосов
/ 11 января 2010

Я согласен с 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);
  }
...