C# 挂线问题

C# 挂线问题,c#,multithreading,events,C#,Multithreading,Events,我正在研究C#如何处理线程和事件。为了做到这一点,我编写了一个简单的程序来模拟温度计和三轴编码器(目前一切都是随机生成的数据)。然后我写了一些事件:开始生成/读取编码器和温度计,在列表框中写入一个字符串(如果温度超过阈值),在日志文件中记录温度数据,根据温度值使用不同的字符串 为此,我编写了4个不同的类,如下所示: 第一:windows窗体类Form1.cs using System; using System.Windows.Forms; using Sensors; namespace T

我正在研究C#如何处理线程和事件。为了做到这一点,我编写了一个简单的程序来模拟温度计和三轴编码器(目前一切都是随机生成的数据)。然后我写了一些事件:开始生成/读取编码器和温度计,在列表框中写入一个字符串(如果温度超过阈值),在日志文件中记录温度数据,根据温度值使用不同的字符串

为此,我编写了4个不同的类,如下所示:

第一:windows窗体类Form1.cs

using System;
using System.Windows.Forms;
using Sensors;

namespace TestWindowsFormsApp
{
public partial class Form1 : Form
{

    private Termometer termometro;
    private AxisPosition assi;

    public Form1()
    {
        InitializeComponent();
        termometro = new Termometer();
        termometro.HighTemperatureReached += Termometro_HighTemperatureReached;
        new TemperatureLogger(termometro); 
        this.FormClosing += Form1_FormClosing; 
        assi = new AxisPosition();
        assi.PositionRead += Assi_PositionRead;
    }

    private void Assi_PositionRead(int x, int y, int z)
    {
        XValue.Invoke((MethodInvoker)delegate {
            XValue.Text = $"{x}";
            YValue.Text = $"{y}";
            ZValue.Text = $"{z}";
        });
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        termometro.HighTemperatureReached -= Termometro_HighTemperatureReached;
        assi.PositionRead -= Assi_PositionRead;
        termometro.StopReading();
        assi.StopReading();
    }

    private void Termometro_HighTemperatureReached(int value)
    {
        AlertsListBox.Invoke((MethodInvoker)delegate
        {
            AlertsListBox.Items.Add($"Temperatura alta, {value} °C");
        });
    }

    private void startButton_Click(object sender, EventArgs e)
    {
        try
        {
            termometro.StartReading();       
        }
        catch (InvalidOperationException exeption)
        {
            MessageBox.Show(exeption.Message);
        }
    }

    private void AxisResetButton_Click(object sender, EventArgs e)
    {
        //TODO: IMPLEMENT
    }

    private void AxisReadStart_Click(object sender, EventArgs e)
    {
        try
        {
            assi.StartReading();
        }
        catch (InvalidOperationException exeption)
        {
            MessageBox.Show(exeption.Message);
        }
    }
}
}
第二:温度记录器类,TemperatureLogger.cs

using System;
using System.IO;
using Sensors;

namespace TestWindowsFormsApp
{
public class TemperatureLogger
{
    public TemperatureLogger(Termometer termometro)
    {
        termometro.TemperatureRead += Termometro_TemperatureRead;
        termometro.HighTemperatureReached += Termometro_HighTemperatureReached;
    }

    private void Termometro_HighTemperatureReached(int value)
    {

        File.AppendAllText("temperature.log", $"{DateTime.Now} - !!!WARNING!!! Temperature read: {value} °C\n");
    }

    private void Termometro_TemperatureRead(int value)
    {
        File.AppendAllText("temperature.log", $"{DateTime.Now} - Temperature read: {value} °C\n");
    }
}
}
第三:AxisPosition类AxisPosition.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Sensors
{
    public class AxisPosition
    {
        private Thread readingThread;
        private bool isReading = false;
        public delegate void AxisNotification(int x, int y, int z);
        public event AxisNotification PositionRead;
        public event AxisNotification PositionSet;
        private int x_ref = 0;
        private int y_ref = 0;
        private int z_ref = 0;

        public void StartReading()
        {
            if (readingThread != null)
                throw new InvalidOperationException("Positioner is already reading!");

            readingThread = new Thread(() =>
            {
                while (isReading)
                {
                    var x = new Random().Next(1000) - x_ref;
                    var y = new Random().Next(300) - y_ref;
                    var z = new Random().Next(600) - z_ref;

                    if (PositionRead != null)
                        PositionRead(x, y, z);

                    if (PositionSet != null)
                        PositionSet(x, y, z);

                    Thread.Sleep(200);
                }
                Console.WriteLine("Exit from axes Thread.");
            });

            isReading = true;
            readingThread.Priority = ThreadPriority.Lowest;
            readingThread.Start();

        }


        public void StopReading()
        {

            isReading = false;
            if (readingThread != null)
            {
                int watchdog = 0;
                while (readingThread.IsAlive && watchdog <= 50)
                {
                    watchdog++;
                    Thread.Sleep(100);
                }
                readingThread = null;
            }
        }    
    }
}
一切正常,但我在关闭窗体窗口时遇到了一个问题:有时(似乎是随机的)某些线程仍然挂起,我必须手动关闭(因此使用Abort()方法)。这是一个强力的解决方法,但我更愿意让线程优雅地退出,就像我在axes one中所做的那样。 我的问题是:为什么有时线程没有退出,但仍然挂起

通过一些调试,似乎没有正确地解释条件isreagind==false,但我不理解为什么


谢谢您的帮助。

当您关闭表单窗口时,它卡在哪里了。基本上,调用事件isclose,线程运行条件设置为false,它应该正常关闭。这种情况有时不会发生。而应用程序则一直处于停滞状态。我通过使用Thread.Abort()方法找到了一个转机,但我想知道发生了什么,以防止在生产阶段出现这种行为。XValue.Invoke()是一个等待发生的死锁。如果它发生在UI线程调用StopReading()时,那么您将遇到问题。总是喜欢BeginInvoke()。这不是唯一的问题,类似bool的isReading不是同步对象,工作线程可能永远不会看到它变为false。这里没有明显的理由说明线程为什么有用,它们所做的只是睡眠。换个简单的计时器,在工具箱里找到一个。谢谢你的回答!然而,我有几个问题:为什么会出现僵局?如果我理解事情是如何工作的,那么这些方法只是调用表单GUI的更新,所以即使表单不再存在,它们也应该异常关闭,而不是挂起。。。我说得对吗?此外,bool值怎么会失败?只要gui存在,它就会工作。。。你能解释一下吗?我知道在这里使用线程是。。不是最优的,但这是一个针对事件和线程的实践应用程序。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Sensors
{
    public class Termometer
    {

        private Thread readingThread;
        public void StartReading()
        {

            if (readingThread != null)
                throw new InvalidOperationException("Termometer is already reading!");

            readingThread = new Thread(() =>
                        {
                            try
                            {
                                while (true)
                                {
                                    var read = new Random().Next(100);

                                    if (read > 50 && HighTemperatureReached != null)
                                        HighTemperatureReached(read);

                                    if (TemperatureRead != null)
                                        TemperatureRead(read);

                                    Thread.Sleep(200);
                                }
                            }
                            catch (ThreadAbortException) {
                                // Clean up
                            }

                            Console.WriteLine("Esco dal Thread di lettura temperature.");
                        }
                       );

            readingThread.Priority = ThreadPriority.Lowest;
            readingThread.Start();

        }
        public event TemperatureNotificationDelegate HighTemperatureReached;
        public event TemperatureNotificationDelegate TemperatureRead;    

        public void StopReading()
        {
            if (readingThread != null)
            {
                readingThread.Abort();
                readingThread = null;
            }
        }    
    }
}