我正在研究C#如何处理线程和事件。为了做到这一点,我写了一个简单的程序,模拟温度计和三轴编码器(目前一切都只是随机生成的数据)。然后我写了一些事件:开始生成/读取编码器和温度计,如果温度超过阈值,在列表框中写入字符串,将温度数据记录在日志文件中,根据温度值使用不同的字符串。
为了做到这一点,我写了4个不同的类,它们是:
1st: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");
}
}
}
3rd:Axes位置类,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;
}
}
}
}
最后,第4课,一读温度,Termometer.cs
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;
}
}
}
}
一切正常,但我关闭窗体窗口时遇到问题:有时(似乎是随机的)某些线程仍然挂起,我必须手动关闭(因此使用Abort()方法)。这是一个强力解决方法,但我更喜欢让线程优雅地退出,就像我在轴1中所做的那样。 我的问题是:为什么有时线程不会退出,但仍然挂起?
似乎通过doind一些调试条件isreagind == false没有得到正确的阐述,但我无法理解为什么。
感谢您的协助。