С# Parallel. Foreach - как красиво остановить?

110
13 октября 2021, 02:00

В общем есть цикл, в нём что-то параллельно делается, и при определённых условиях нужно его остановить, и что-то на форме занести в текстбоксы, после чего программа считывает эти текстбоксы.
Нашёл что-то про ManualResetEventSlim, но он блочит все потоки, и с формой не поработаешь (висит). Наколхозил в таком виде (в том месте, где нужно остановить):

    ManualResetEventSlim mres.Reset();
    if(MainProcID == Thread.CurrentThread.ManagedThreadId) //MainProcID получаю при
    // инициализации формы
    {
      while (!CF) //булевый флаг для бесконечного цикла, пока кнопку не 
      //нажму
      {
        Application.DoEvents();
      }
      //тут что-нибудь делаю
      mres.set();
    }
    else
    mres.Wait();

Но как это покрасивее сделать? Тут ещё, полагаю, будут проблемы, если 2 потока решат её остановить - непредсказуемый результат - надо ещё флагами защищаться. Ну, соответственно - потом надо запустить по нажатию кнопки.

Весь цикл

Parallel.ForEach(DRList, new ParallelOptions { MaxDegreeOfParallelism = 5 }, DR =>
        {
            ClassSrz srz1 = new ClassSrz("SRZ-01", DBSelected);
            i++;
            //reportProgress(i * 100 / Col);

            int C = srz1.ExecuteNonQueryR("update ZAGS_PARENT_INFO set PID=" + DR["ID"].ToString() +
                  " where DR=" + strToSQL(DR["DR"].ToString(), date: true, last: true) +
                  " and docn=" + strToSQL(DR["DOCN"].ToString(), last: true) +
                  " and DOCS=" + strToSQL(DR["DOCS"].ToString(), last: true) +
                  " and docdt=" + strToSQL(DR["DOCDT"].ToString(), date: true, last: true) +
                  " and FAM_NEW=" + strToSQL(DR["FAM"].ToString(), last: true) +
                  " and IM_NEW=" + strToSQL(DR["IM"].ToString(), last: true) +
                  " and OT_NEW=" + strToSQL(DR["OT"].ToString(), last: true) +
                  " and PID is null"
                   );
            int CWNew = srz1.ExecuteNonQueryR("update ZAGS_BRAK_INFO set PID_W_NEW=" + DR["ID"].ToString() +
                " where DR_W=" + strToSQL(DR["DR"].ToString(), date: true, last: true) +
                " and docn_W=" + strToSQL(DR["DOCN"].ToString(), last: true) +
                " and DOCS_W=" + strToSQL(DR["DOCS"].ToString(), last: true) +
                " and docdt_W=" + strToSQL(DR["DOCDT"].ToString(), date: true, last: true) +
                " and FAM_W_NEW=" + strToSQL(DR["FAM"].ToString(), last: true) +
                " and IM_W=" + strToSQL(DR["IM"].ToString(), last: true) +
                " and OT_W=" + strToSQL(DR["OT"].ToString(), last: true) +
                " and PID_W_NEW is null"
                 );
            int CMNew = srz1.ExecuteNonQueryR("update ZAGS_BRAK_INFO set PID_M_NEW=" + DR["ID"].ToString() +
                " where DR_M=" + strToSQL(DR["DR"].ToString(), date: true, last: true) +
                " and docn_M=" + strToSQL(DR["DOCN"].ToString(), last: true) +
                " and DOCS_M=" + strToSQL(DR["DOCS"].ToString(), last: true) +
                " and docdt_M=" + strToSQL(DR["DOCDT"].ToString(), date: true, last: true) +
                " and FAM_M_NEW=" + strToSQL(DR["FAM"].ToString(), last: true) +
                " and IM_M=" + strToSQL(DR["IM"].ToString(), last: true) +
                " and OT_M=" + strToSQL(DR["OT"].ToString(), last: true) +
                " and PID_M_NEW is null"

                 );
            int CMOld = srz1.ExecuteNonQueryR("update ZAGS_BRAK_INFO set PID_M_OLD=" + DR["ID"].ToString() +
            " where DR_M=" + strToSQL(DR["DR"].ToString(), date: true, last: true) +
            " and docn_M=" + strToSQL(DR["DOCN"].ToString(), last: true) +
            " and DOCS_M=" + strToSQL(DR["DOCS"].ToString(), last: true) +
            " and docdt_M=" + strToSQL(DR["DOCDT"].ToString(), date: true, last: true) +
            " and FAM_M_OLD=" + strToSQL(DR["FAM"].ToString(), last: true) +
            " and IM_M=" + strToSQL(DR["IM"].ToString(), last: true) +
            " and OT_M=" + strToSQL(DR["OT"].ToString(), last: true) +
            " and PID_M_OLD is null"

             );
            int CWOld = srz1.ExecuteNonQueryR("update ZAGS_BRAK_INFO set PID_W_OLD=" + DR["ID"].ToString() +
             " where DR_W=" + strToSQL(DR["DR"].ToString(), date: true, last: true) +
             " and docn_W=" + strToSQL(DR["DOCN"].ToString(), last: true) +
             " and DOCS_W=" + strToSQL(DR["DOCS"].ToString(), last: true) +
             " and docdt_W=" + strToSQL(DR["DOCDT"].ToString(), date: true, last: true) +
             " and FAM_W_OLD=" + strToSQL(DR["FAM"].ToString(), last: true) +
             " and IM_W=" + strToSQL(DR["IM"].ToString(), last: true) +
             " and OT_W=" + strToSQL(DR["OT"].ToString(), last: true) +
             " and PID_W_OLD is null"

              );
            if (ColPar == 1)
            {
                mres.Reset();
                if (MainProcID == Thread.CurrentThread.ManagedThreadId)
                {
                    CF = false;
                    while (!CF)
                    {
                        Application.DoEvents();
                    }
                }
                else
                    mres.Wait();
            }
            BeginInvoke((Action)(() =>
            {
                pB1.Value = i; ColPar += C; ColBracWNew += CWNew; ColBracMNew += CMNew;
                ColBracWOld += CWOld; ColBracMOld += CMOld;
                //Останавливаем для выбора
            }));
            Application.DoEvents();
            //   Thread.Sleep(100);                             
        }
        );

Вот кнопка

 private void ChangeFlag_Click(object sender, EventArgs e)
    {
        CF = true;
        if (mres.IsSet)
            mres.Reset();
        else
            mres.Set();
    }

P.S. условие if (ColPar == 1) просто для теста поставил пока

Answer 1

public partial class MainForm : Form
{
    //источник токена отмены
    private CancellationTokenSource _cts;
    public MainForm()
    {
        InitializeComponent();
        StartPosition = FormStartPosition.CenterScreen;
        Text = "Пример";
        _buttonCancel.Click += ButtonCancel_Click;
        _buttonStart.Click += ButtonStart_Click;
    }
    private async void ButtonStart_Click(object sender, EventArgs e)
    {
        //готовим запуск
        _textBoxOutput.Text = String.Empty;
        _buttonCancel.Enabled = true;
        _buttonStart.Enabled = false;
        //собираем данные
        var inputs = new string[] { _textBoxFirst.Text, _textBoxSecond.Text, _textBoxThird.Text };
        //новый источник токена отмены
        _cts = new CancellationTokenSource();
        //опции для параллельного запуска
        var po = new ParallelOptions();
        //передаем токен отмены
        po.CancellationToken = _cts.Token;
        try
        {
            //запускаем задачей чтоб освободить UI
            await Task.Run(() => RunParallel(inputs, po));
        }
        finally
        {
            //настраиваем по окончании вычисления
            _buttonCancel.Enabled = false;
            _buttonStart.Enabled = true;
            _cts.Dispose();
        }
    }
    private void ButtonCancel_Click(object sender, EventArgs e)
    {
        //отправляем запрос на отмену
        _cts.Cancel();
    }
    private void RunParallel(string[] inputs, ParallelOptions po)
    {
        try
        {
            Parallel.ForEach(inputs, po, (string t, ParallelLoopState state) =>
            {
                //если был запрос на отмену, останавливаем цикл
                if (po.CancellationToken.IsCancellationRequested)
                {
                    state.Stop();
                }
                //получаем данные
                var text = GetText(t, po.CancellationToken) + Environment.NewLine;
                //отображаем данные
                this.BeginInvoke((Action)(() => _textBoxOutput.Text += text));
            });
        }
        catch (Exception ex)
        {
            this.BeginInvoke((Action)(() => _textBoxOutput.Text += ex.Message));
        }
    }
    //метод, который типа что-то вычисляет
    private string GetText(string t, CancellationToken cancellationToken)
    {
        var result = String.Empty;
        for (int i = 0; i < 5; i++)
        {
            //бросаем исключение в случае отмены
            cancellationToken.ThrowIfCancellationRequested();
            Thread.Sleep(500);
            result += $"{t}[{i}]";
        }
        return result;
    }
}
READ ALSO
Как будет выглядеть часть кода vba на c#?

Как будет выглядеть часть кода vba на c#?

Подскажите как будет выглядеть эти два кусочка кода на c#?

209
Модульный тест класса

Модульный тест класса

при написании модульного теста возникает ошибка "object не содержит определения для qwerty"

152
Стоит ли боятся DownCast&#39;а(конкретизация типа)?

Стоит ли боятся DownCast'а(конкретизация типа)?

Допустим, есть 3 интерфейса:

146
конфликт при миграции Code First

конфликт при миграции Code First

Подскажите в чем может быть проблема, вылетает ошибка при миграции на добавление связи между таблицами

136