C#, parallel loop, output to richtextbox

283
09 декабря 2016, 08:58

Есть код:

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

У нас "многопоточность", из-за которой происходит конфликт потоков в записи в rich в этой строке richTextBox2.AppendText(oa); Как можно сделать так, чтобы сохранить "многопоточность", но всё же результаты записывались в rich?

Answer 1

Дело в том, что тело функции, которую вы передаете в Parallel.ForEach() выполняется в других потоках. Внутри этой функции вы обращаетесь к UI-компоненту. Но UI-компоненты позволяют с работать с собой только в том же потоке, в котором они были созданы. Используя проверку IsInvokeRequired и вызов Invoke(), вы "перенаправляете" обращение к компоненту в главный (UI) поток и таким образом решаете проблему.

Кроме того, как указали в комментариях, у вас в коде "масло масляное". Дело в том, что вызов Task.Factory.StartNew() выполняет переданную ему функцию в отдельно потоке. Однако этот вызов уже происходит в отдельном потоке, поскольку вы используете Parallel.ForEach(). К этому добавляется еще проблема обработки исключений: делегат, который вы передаете в Parallel.ForEach() представляет собой async void и опасен тем, что вы либо не узнаете о возникшем исключении, либо узнаете, но будет поздно -- процесс упадет.

Нужно либо убрать вызов Task.Factory.StartNew() (внешняя переменная вам при этом тоже не нужна):

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

Либо убрать Parallel.ForEach() (этот вариант будет работать медленнее, потому что мы выполняем долгую операцию в другом потоке одну за другой, а не сразу пачкой, как это происходит в случае с Parallel.ForEach()):

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

Также для подобного кода как правило принято разделять шаг "делаем что-то в отдельном потоке" и шаг "выводим результат на UI". Например, если пользоваться async/await, на помощь придет класс Progress<T>.

Answer 2

Проблема решена, дописываю с помощью функции:

 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;
        }
    }
READ ALSO
Работа с combobox

Работа с combobox

Доброго времени сутокВозникла следующая проблема, есть XML файл, у меня получается его считать и записать данные в ComboBox, хотелось что бы при...

618
Как выполнить действие по кнопке при свернутой форме c#

Как выполнить действие по кнопке при свернутой форме c#

Как сделать какое либо действие на клавишу ESC если программа свернута?

346
Не конвертируется переменная из string в int

Не конвертируется переменная из string в int

У меня есть взятая из буфера обмена скопированная из браузера 194 и она не конвертируется, пишет

367