Click here to Skip to main content
15,885,914 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
When using Winform and Multithread together, the thread was terminated with a button. You must click the button several times to exit.
The program seems to miss the button event. Can't the program respond to click one?
Sorry, I'm not very good at English.

What I have tried:

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());
                }
            }
        }
    }
}
Posted
Updated 18-Jul-19 21:21pm
v2
Comments
BillWoodruff 19-Jul-19 1:20am    
Have you tried using thread.Interrupt followed by thread.Stop ? Or, tried thread.Abort ?
Member 14509405 19-Jul-19 17:50pm    
It is similar the result of Interrupt, Abort.

The methods
Interrupt
and
C#
Abort
should be used with caution as the documentation says: Thread.Abort Method (System.Threading) | Microsoft Docs[^]
A better approach is to let the thread end gracefully by setting a flag.

Please make sure the flag is marked by the volatile keyword.
This must be done because the flag variable is accessed by two threads which is the main thread and the thread you created yourself.
The volatile keyword tells the compiler not to optimize access to the flag variable.
This makes sure that always the most current value is available.
Please refer to volatile - C# Reference | Microsoft Docs[^]

Please have a look at this example:
It uses a form with two Buttons named buttonStart and buttonStop as well as a ListBox named "listBox".

The code behind looks like this:

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);
            }
        }
    }
}

The ListBox will be filled with numbers when you have clicked the Start button. The UI stays responsive.
When clicking the Stop button the cancel flag is set to true.

Make also sure not to call UI Elements directly from a different thread.
Windows Forms is not thread-safe.
You must use Invoke for this.
First you must check if the ListBox is updated from a different thread by using InvokeRequired. In this specific case InvokeRequired will return true, as you are updating the ListBox from a different thread.
You then invoke a delegate of AddText which will update the ListBox from the UI thread thus InvokeRequired will return false now.

An explanation of how to update the UI from a different thread can be found here How to: Make thread-safe calls to Windows Forms controls | Microsoft Docs[^]
 
Share this answer
 
v4
Comments
BillWoodruff 20-Jul-19 3:55am    
A really good answer ! I voted this #4, and look forward to voting it #5 when you:

1. describe what 'volatile does here

2. describe what ListBox.InvokeRequired does here

imho, these additions can be short.

Why do I ask you to do this: because I think it will increase the educational value for newcomers to C# threading, or those with little experience with it.

cheers, Bill

p.s. I mentioned 'Interrupt and 'Abort with the idea of diagnosing the problem the OP described ... not as a "solution."
TheRealSteveJudge 22-Jul-19 2:53am    
Hi Bill,
thank you for the encouraging comment and valuable hints.
I updated the solution accordingly.
Best regards,
Stephan
BillWoodruff 22-Jul-19 2:55am    
now +5 :) cheers, Bill
TheRealSteveJudge 22-Jul-19 3:09am    
Thank you Bill!
Member 14509405 22-Jul-19 23:11pm    
Thank you very much!! The result of test is good.
C#
_clitThread.Join(10);
_clitThread = null;

You are ignoring the return value from Thread.Join, so you have no idea whether the thread has terminated or not. Please use return values to ensure you know what your code is actually doing: Thread.Join Method (System.Threading) | Microsoft Docs[^]
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900