Member 14509405 Ответов: 2

Как остановить многопоточность с помощью кнопки GUI


При совместном использовании Winform и Multithread поток завершался кнопкой. Вы должны нажать кнопку несколько раз, чтобы выйти.
Программа, кажется, пропустила событие кнопки. Разве программа не может ответить на щелчок один?
Извините, я не очень хорошо говорю по-английски.

Что я уже пробовал:

using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Net.Sockets;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Client
{
    public partial class Client : Form
    {
        Thread _clitThread;
        uint counter = 0;
        private bool stopEvt = false;
        private bool run = false;
        public delegate void LogWriteDelegate(string msg);

        public Client()
        {
            InitializeComponent();
        }

        private void btnConn_Click(object sender, EventArgs e)
        {
            run = true;
            _clitThread = new Thread(new ThreadStart(ClientReceive));
            _clitThread.IsBackground = true;
            _clitThread.Start();
        }

        public void MessageWrite(string msg)
        {
            LogWriteDelegate deleLogWirte = new LogWriteDelegate(AppendMsg);
            this.Invoke(deleLogWirte, new object[] { msg });
        }

        public void AppendMsg(string msg)
        {
            txtOutput.AppendText(msg + "\r\n");
            txtOutput.Focus();
            txtOutput.ScrollToCaret();
        }

        private void ClientReceive()
        {
            try
            {
                while (run)
                {
                    Thread.Sleep(100);
                    MessageWrite((++counter).ToString());
                }
            }
            catch (Exception ex)
            {
                MessageWrite(ex.ToString());
            }
            finally
            {
            }
        }

        private void BtnStop_Click(object sender, EventArgs e)
        {
            stopEvt = true;
            run = false;
            if (_clitThread != null && _clitThread.IsAlive)
            {
                try
                {
                    _clitThread.Join(10);
                    _clitThread = null;
                    stopEvt = false;
                    MessageWrite("stop thread");
                }
                catch (Exception ex)
                {
                    MessageWrite(ex.ToString());
                }
            }
        }
    }
}

BillWoodruff

Вы пробовали использовать нить?Прерывание, за которым следует поток.Остановиться ? Или, попробовал нить.Прервать ?

Member 14509405

Это похоже на результат прерывания, прерывания.

2 Ответов

Рейтинг:
5

Richard MacCutchan

_clitThread.Join(10);
_clitThread = null;

Вы игнорируете возвращаемое значение из Thread.Join, таким образом, вы понятия не имеете, закончился ли поток или нет. Пожалуйста, используйте возвращаемые значения, чтобы убедиться, что вы знаете, что на самом деле делает ваш код: Нить.Метод Соединения (System.Продевать Нитку) | Майкрософт Документы[^]


Рейтинг:
18

TheRealSteveJudge

Метод

Interrupt
и
Abort
следует использовать с осторожностью, как говорится в документации: Нить.Метод Прерывания (System.Продевать Нитку) | Майкрософт Документы[^]
Лучший подход-позволить потоку закончиться изящно, установив флаг.

Пожалуйста, убедитесь, что флаг помечен ключевым словом volatile.
Это необходимо сделать, потому что переменная флага доступна двум потокам - основному потоку и потоку, который вы создали сами.
Ключевое слово volatile указывает компилятору не оптимизировать доступ к переменной flag.
Это гарантирует, что всегда доступно самое актуальное значение.
Пожалуйста, обратитесь к volatile - Справочник по C# | Microsoft Docs[^]

Пожалуйста, взгляните на этот пример:
Он использует форму с двумя кнопками buttonStart и buttonStop, а также список с именем "listBox".

Код, стоящий за этим, выглядит следующим образом:

using System;
using System.Threading;
using System.Windows.Forms;

namespace ThreadTest
{
    public partial class Form1 : Form
    {
        private volatile bool cancel;
        private Thread thread;
        private delegate void AddTextDelegate(string text);

        public Form1()
        {
            InitializeComponent();

            buttonStop.Enabled = false;
        }

        private void ButtonStart_Click(object sender, EventArgs e)
        {
            buttonStart.Enabled = false;
            buttonStop.Enabled = true;

            cancel = false;

            thread = new Thread(DoSomething);

            thread.Start();
        }

        private void ButtonStop_Click(object sender, EventArgs e)
        {
            cancel = true;

            thread.Join();
            thread = null;

            buttonStart.Enabled = true;
            buttonStop.Enabled = false;
        }

        private void DoSomething()
        {
            var number = 0;

            while (!cancel)
            {
                AddText($"{number++}");

                Thread.Sleep(500);
            }
        }

        private void AddText(string text)
        {
            if (listBox.InvokeRequired)
            {
                var d = new AddTextDelegate(AddText);

                Invoke(d, text);
            }
            else
            {
                listBox.Items.Add(text);
            }
        }
    }
}

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

Также убедитесь, что вы не вызываете элементы пользовательского интерфейса непосредственно из другого потока.
Windows Forms не является потокобезопасной.
Для этого вы должны использовать Invoke.
Сначала вы должны проверить, обновляется ли список из другого потока с помощью InvokeRequired. В этом конкретном случае InvokeRequired вернет true, так как вы обновляете список из другого потока.
Затем вы вызываете делегат AddText, который обновит список из потока пользовательского интерфейса, поэтому InvokeRequired теперь вернет false.

Объяснение того, как обновить пользовательский интерфейс из другого потока, можно найти здесь Как сделать потокобезопасные вызовы элементов управления Windows Forms | Microsoft Docs[^]


BillWoodruff

Действительно хороший ответ ! Я проголосовал за это № 4 и с нетерпением жду голосования за это № 5, Когда вы:

1. опишите, что здесь делает volatile

2. опишите, что такое ListBox.Свойство invokerequired здесь

имхо, эти дополнения могут быть короткими.

Почему я прошу вас сделать это: потому что я думаю, что это увеличит образовательную ценность для новичков в потоковой передаче C# или тех, кто имеет мало опыта в этом.

твое здоровье, Билл

p.s. я упомянул "прерывание" и "прерывание" с идеей диагностики проблемы, описанной ОП ... не в качестве "решения."

TheRealSteveJudge

Привет Билл,
спасибо за обнадеживающий комментарий и ценные советы.
Я обновил решение соответствующим образом.
С уважением,
Стефан

BillWoodruff

теперь +5 :) ура, Билл

TheRealSteveJudge

Спасибо, Билл!

Member 14509405

Большое спасибо!! Результат теста хороший.