There is a code:

string oa = ""; Parallel.ForEach(richTextBox1.Lines, async item => { oa = await Task.Factory.StartNew( () => Wor.SomeLongOperation(item), TaskCreationOptions.LongRunning) +"\n"; richTextBox2.AppendText(oa); }); 

We have "multithreading", because of which there is a conflict of threads in the record in rich in this line richTextBox2.AppendText(oa); How can you do so to preserve the "multithreading", but still the results were recorded in rich ?

  • I answered something similar: ru.stackoverflow.com/questions/547493/… - iluxa1810
  • one
    You do something weird. Parallel.ForEach and asynchronous methods are not friendly. - Pavel Mayorov
  • I need multithreading, but at the same time so that the form does not hang, Paul. - Egor Randomize

2 answers 2

The point is that the body of the function that you pass to Parallel.ForEach() is executed in other threads. Inside this function, you access the UI component. But UI-components allow you to work with yourself only in the same thread in which they were created. Using the IsInvokeRequired check and the Invoke() call, you "redirect" the call to the component to the main (UI) thread and thus solve the problem.


In addition, as indicated in the comments, you have in the code "oil oil". The point is that the Task.Factory.StartNew() call performs the function passed to it in a separate thread. However, this call is already happening in a separate thread , since you are using Parallel.ForEach() . To this is added the problem of handling exceptions: the delegate that you pass to Parallel.ForEach() is async void and is dangerous because you either don’t know about the exception, or you learn, but it’s too late - the process will fail.

You must either remove the call to Task.Factory.StartNew() (you do not need the external variable, either):

 Parallel.ForEach(richTextBox1.Lines, item => { var oa = Wor.SomeLongOperation(item) + "\n"; // вызов richTextBox2.AppendText(oa) через Invoke }); 

Either remove Parallel.ForEach() (this option will work slower, because we perform a long operation in another stream one after another, and not immediately with a bundle, as is the case with Parallel.ForEach() ):

 foreach (var item in richTextBox1.Lines) { var oa = await Task.Factory.StartNew( () => Wor.SomeLongOperation(item), TaskCreationOptions.LongRunning) +"\n"; richTextBox2.AppendText(oa); }; 

Also, for such a code, it is usually accepted to divide the step “do something in a separate thread” and step “output the result to the UI”. For example, if you use async / await, the class Progress<T> comes to the rescue.

  • Unfortunately, this is not the answer yet - the first method will still give an error message, and the second method will work consistently (apparently, my fault - confused you with your comment). - Pavel Mayorov
  • In the first method, you need to add Invoke - and in the second, it's better to use something like await Task.WhenAll(richTextBox1.Lines.Select(async ... )); - Pavel Mayorov
  • @PavelMayorov yes, it is clear that in the second case Task.WhenAll() is better, however I decided not to load the author. - andreycha

The problem is solved, I add using the function:

  public void AddText(string text) { if (this.richTextBox1.InvokeRequired) { Action<string> updaterdelegate = new Action<string>(AddText); try { this.Invoke(updaterdelegate, new object[] { text }); } catch (ObjectDisposedException ex) { } } else { richTextBox2.Text += text; } } 
  • 2
    Too hard. There are ways easier. - Pavel Mayorov