移除DeviceManager.cs中不必要的锁操作以优化性能

This commit is contained in:
zqm
2025-11-03 11:14:17 +08:00
parent b82f650754
commit 882d15d4ea

View File

@@ -266,6 +266,42 @@ namespace JoyD.Windows.CS.Toprie
private Thread _temperatureReceiveThread; private Thread _temperatureReceiveThread;
private bool _isReceivingTemperatureData = false; private bool _isReceivingTemperatureData = false;
private bool _isTemperatureReceivingPaused = false; // 暂停标志,用于控制是否处理接收到的温度数据 private bool _isTemperatureReceivingPaused = false; // 暂停标志,用于控制是否处理接收到的温度数据
/// <summary>
/// 准确检测TcpClient连接状态
/// 不仅检查Connected属性还通过Poll方法检测连接是否真正有效
/// </summary>
/// <param name="client">要检查的TcpClient实例</param>
/// <returns>如果连接有效返回true否则返回false</returns>
private bool IsTcpClientConnected(TcpClient client)
{
if (client == null || !client.Connected)
return false;
// 检查连接状态的标准方法
// 100毫秒超时不读取数据只检测状态
try
{
// Poll方法检查连接状态100ms超时
// SelectMode.SelectRead检查是否可读
bool isConnected = !client.Client.Poll(100, System.Net.Sockets.SelectMode.SelectRead) ||
client.Client.Available > 0;
// 如果连接已断开Client.RemoteEndPoint会抛出异常
if (isConnected)
{
// 尝试访问RemoteEndPoint验证连接
var dummy = client.Client.RemoteEndPoint;
}
return isConnected;
}
catch
{
// 任何异常都表示连接可能已断开
return false;
}
}
private ManualResetEvent _stopTemperatureEvent; private ManualResetEvent _stopTemperatureEvent;
private const int TEMPERATURE_TCP_PORT = 8081; // 温度数据TCP端口 - 修正为热像仪SDK文档中指定的端口 private const int TEMPERATURE_TCP_PORT = 8081; // 温度数据TCP端口 - 修正为热像仪SDK文档中指定的端口
@@ -1125,12 +1161,11 @@ namespace JoyD.Windows.CS.Toprie
// 使用局部变量存储资源 // 使用局部变量存储资源
TcpClient localTcpClient = null; TcpClient localTcpClient = null;
NetworkStream localStream = null; NetworkStream localStream = null;
List<byte> temperatureDataAccumulator = new List<byte>(); List<byte[]> temperatureDataAccumulator = new List<byte[]>();
byte[] buffer = new byte[65536]; // 缓冲区大小 byte[] buffer = new byte[65536]; // 缓冲区大小
// 定义常量 // 定义常量
const int RECEIVE_TIMEOUT = 30000; // 增加到30秒减少因超时导致的断开 const int RECEIVE_TIMEOUT = 30000; // 增加到30秒减少因超时导致的断开
const int HEARTBEAT_INTERVAL = 15000; // 心跳间隔15秒
const int MEDIUM_SLEEP_MS = 50; const int MEDIUM_SLEEP_MS = 50;
const int LONG_SLEEP_MS = 2000; // 增加重连等待时间 const int LONG_SLEEP_MS = 2000; // 增加重连等待时间
const int ERROR_SLEEP_MS = 1000; // 增加异常恢复等待时间 const int ERROR_SLEEP_MS = 1000; // 增加异常恢复等待时间
@@ -1165,8 +1200,8 @@ namespace JoyD.Windows.CS.Toprie
temperaturePort = TEMPERATURE_TCP_PORT; temperaturePort = TEMPERATURE_TCP_PORT;
} }
// 连接管理:当连接不存在已断开时创建新连接 // 连接管理:当连接不存在已断开或检测到连接不可用时创建新连接
if (localTcpClient == null || !localTcpClient.Connected || localStream == null) if (localTcpClient == null || !IsTcpClientConnected(localTcpClient) || localStream == null)
{ {
// 清理之前可能存在的连接资源 // 清理之前可能存在的连接资源
try try
@@ -1200,6 +1235,8 @@ namespace JoyD.Windows.CS.Toprie
ReceiveTimeout = RECEIVE_TIMEOUT, ReceiveTimeout = RECEIVE_TIMEOUT,
SendTimeout = RECEIVE_TIMEOUT SendTimeout = RECEIVE_TIMEOUT
}; };
// 设置接收缓冲区大小
localTcpClient.ReceiveBufferSize = 65536; // 64KB
Log($"正在连接到温度数据端口 {temperaturePort}..."); Log($"正在连接到温度数据端口 {temperaturePort}...");
localTcpClient.Connect(deviceIp, temperaturePort); localTcpClient.Connect(deviceIp, temperaturePort);
@@ -1259,7 +1296,7 @@ namespace JoyD.Windows.CS.Toprie
// 记录上次心跳时间和上次接收数据时间 // 记录上次心跳时间和上次接收数据时间
DateTime lastHeartbeatTime = DateTime.Now; DateTime lastHeartbeatTime = DateTime.Now;
DateTime lastReceiveTime = DateTime.Now; DateTime lastReceiveTime = DateTime.Now;
// 持续读取温度数据流 // 持续读取温度数据流
while (localTcpClient != null && localTcpClient.Connected) while (localTcpClient != null && localTcpClient.Connected)
{ {
@@ -1268,32 +1305,122 @@ namespace JoyD.Windows.CS.Toprie
{ {
isPaused = _isTemperatureReceivingPaused; isPaused = _isTemperatureReceivingPaused;
} }
if (isPaused) if (isPaused)
{ {
Log("温度接收已暂停,等待恢复"); Log("温度接收已暂停,等待恢复");
Thread.Sleep(MEDIUM_SLEEP_MS); Thread.Sleep(MEDIUM_SLEEP_MS);
// 在暂停状态下,仍然需要定期检查连接是否有效
if (localTcpClient != null && !IsTcpClientConnected(localTcpClient))
{
Log("暂停状态下检测到连接已断开,将在恢复时重建连接");
try
{
if (localStream != null)
{
localStream.Close();
localStream = null;
}
if (localTcpClient != null)
{
localTcpClient.Close();
localTcpClient = null;
}
}
catch (Exception ex)
{
Log($"关闭无效连接时发生异常: {ex.Message}");
}
}
continue; continue;
} }
else
// 发送心跳保持连接
if ((DateTime.Now - lastHeartbeatTime).TotalMilliseconds > HEARTBEAT_INTERVAL)
{ {
// 从暂停状态恢复时,确保连接仍然有效,必要时重新启动数据传输
if (localTcpClient != null && !IsTcpClientConnected(localTcpClient))
{
Log("恢复接收时检测到连接无效,需要重建连接");
try
{
if (localStream != null)
{
localStream.Close();
localStream = null;
}
if (localTcpClient != null)
{
localTcpClient.Close();
localTcpClient = null;
}
}
catch (Exception ex)
{
Log($"关闭无效连接时发生异常: {ex.Message}");
}
// 跳出内层循环,回到外层循环重新建立连接
break;
}
// 如果连接有效但数据传输可能已停止,尝试重新发送开始命令
// 这是为了解决暂停后恢复时DataAvailable始终为false的问题
try try
{ {
byte[] heartbeatCommand = Encoding.ASCII.GetBytes("heartbeat\r\n"); // 发送开始温度数据传输的命令
localStream.Write(heartbeatCommand, 0, heartbeatCommand.Length); byte[] startCommand = Encoding.ASCII.GetBytes("start_temp_transfer\r\n");
localStream.Write(startCommand, 0, startCommand.Length);
localStream.Flush(); localStream.Flush();
lastHeartbeatTime = DateTime.Now; Log("从暂停状态恢复,重新发送开始温度数据传输命令");
// 心跳不记录日志以减少日志量
} }
catch (Exception ex) catch (Exception ex)
{ {
Log($"发送心跳失败: {ex.Message}"); Log($"重新发送开始命令失败: {ex.Message}");
// 发送失败,可能连接已断开,需要重建连接
try
{
if (localStream != null)
{
localStream.Close();
localStream = null;
}
if (localTcpClient != null)
{
localTcpClient.Close();
localTcpClient = null;
}
}
catch (Exception closeEx)
{
Log($"关闭失败连接时发生异常: {closeEx.Message}");
}
// 跳出内层循环,回到外层循环重新建立连接
break;
} }
} }
// 使用非阻塞方式检查是否有数据可读 // 使用非阻塞方式检查是否有数据可读
// 先检查连接是否仍然有效
if (localTcpClient != null && !IsTcpClientConnected(localTcpClient))
{
Log("检测到连接已断开,准备重建连接");
try
{
if (localStream != null)
{
localStream.Close();
localStream = null;
}
if (localTcpClient != null)
{
localTcpClient.Close();
localTcpClient = null;
}
}
catch (Exception ex)
{
Log($"关闭断开的连接时发生异常: {ex.Message}");
}
continue;
}
if (localStream.DataAvailable) if (localStream.DataAvailable)
{ {
// 有数据可读时进行阻塞读取 // 有数据可读时进行阻塞读取
@@ -1301,9 +1428,7 @@ namespace JoyD.Windows.CS.Toprie
if (bytesRead > 0) if (bytesRead > 0)
{ {
lastReceiveTime = DateTime.Now; // 更新最后接收数据时间 lastReceiveTime = DateTime.Now; // 更新最后接收数据时间
byte[] receivedBytes = new byte[bytesRead]; Log($"==========================================接收到温度数据字节数: {bytesRead}");
Array.Copy(buffer, receivedBytes, bytesRead);
Log($"接收到温度数据字节数: {bytesRead}");
// 根据暂停状态决定是否处理数据 // 根据暂停状态决定是否处理数据
if (isPaused) if (isPaused)
@@ -1312,39 +1437,46 @@ namespace JoyD.Windows.CS.Toprie
} }
else else
{ {
byte[] receivedBytes = new byte[bytesRead];
Array.Copy(buffer, receivedBytes, bytesRead);
// 同步接收并处理数据 // 同步接收并处理数据
lock (temperatureDataAccumulator) temperatureDataAccumulator.Add(receivedBytes);
{ ProcessReceivedTemperatureData(temperatureDataAccumulator);
temperatureDataAccumulator.AddRange(receivedBytes);
ProcessReceivedTemperatureData(temperatureDataAccumulator);
}
} }
} }
else if (bytesRead == 0) else if (bytesRead == 0)
{
// 连接已关闭
Log("远程主机关闭了连接");
// 重置状态标志,下一次循环会创建新连接
lock (_lockObject)
{ {
// 连接已关闭 _isReceivingTemperatureData = false;
Log("远程主机关闭了连接"); }
// 重置状态标志,下一次循环会创建新连接 // 清理连接资源
lock (_lockObject) try
{ {
_isReceivingTemperatureData = false; if (localStream != null)
}
// 清理连接资源
try
{ {
localStream.Close(); localStream.Close();
localTcpClient.Close(); localStream = null;
}
if (localTcpClient != null)
{
localTcpClient.Close();
localTcpClient = null;
} }
catch {}
localStream = null;
localTcpClient = null;
continue;
} }
catch { }
localStream = null;
localTcpClient = null;
continue;
} }
} }
}
// while循环退出后的处理 // while循环退出后的处理
} }
catch (Exception ex) catch (Exception ex)
@@ -1422,43 +1554,43 @@ namespace JoyD.Windows.CS.Toprie
/// 处理接收到的温度数据 /// 处理接收到的温度数据
/// </summary> /// </summary>
/// <param name="dataAccumulator">累积的温度数据</param> /// <param name="dataAccumulator">累积的温度数据</param>
private void ProcessReceivedTemperatureData(List<byte> dataAccumulator) private void ProcessReceivedTemperatureData(List<byte[]> dataAccumulator)
{ {
// 根据TemperatureData类的要求每个温度帧包含9字节头部 + 温度数据 //根据TemperatureData类的要求每个温度帧包含9字节头部 + 温度数据
// 根据注释设备实际提供的数据分辨率应为256x192最终映射到512x384显示 // 根据注释设备实际提供的数据分辨率应为256x192最终映射到512x384显示
const int HEADER_SIZE = 9; // 9字节头部("+TEMP"+数据长度) const int HEADER_SIZE = 9; // 9字节头部("+TEMP"+数据长度)
const int WIDTH = 256; const int WIDTH = 256;
const int HEIGHT = 192; const int HEIGHT = 192;
const int TEMPERATURE_DATA_FRAME_SIZE = HEADER_SIZE + WIDTH * HEIGHT * 2; // 9字节头部 + 每个温度值2字节 const int TEMPERATURE_DATA_FRAME_SIZE = HEADER_SIZE + WIDTH * HEIGHT * 2; // 9字节头部 + 每个温度值2字节
try try
{ {
Log($"开始处理温度数据,当前累积数据量: {dataAccumulator.Count} 字节,所需帧大小: {TEMPERATURE_DATA_FRAME_SIZE} 字节"); Log($"开始处理温度数据,当前累积数据量: {dataAccumulator.Count} 字节,所需帧大小: {TEMPERATURE_DATA_FRAME_SIZE} 字节");
// 检查是否有足够的数据构成完整的温度数据帧 // 检查是否有足够的数据构成完整的温度数据帧
while (dataAccumulator.Count >= TEMPERATURE_DATA_FRAME_SIZE) while (dataAccumulator.Count >= TEMPERATURE_DATA_FRAME_SIZE)
{ {
Log($"找到完整的温度数据帧,开始处理"); Log($"找到完整的温度数据帧,开始处理");
// 提取一帧温度数据 // 提取一帧温度数据
byte[] temperatureFrame = dataAccumulator.GetRange(0, TEMPERATURE_DATA_FRAME_SIZE).ToArray(); byte[] temperatureFrame = dataAccumulator.GetRange(0, TEMPERATURE_DATA_FRAME_SIZE).ToArray();
dataAccumulator.RemoveRange(0, TEMPERATURE_DATA_FRAME_SIZE); dataAccumulator.RemoveRange(0, TEMPERATURE_DATA_FRAME_SIZE);
Log($"提取温度数据帧完成,剩余累积数据量: {dataAccumulator.Count} 字节"); Log($"提取温度数据帧完成,剩余累积数据量: {dataAccumulator.Count} 字节");
// 获取温度补偿值 // 获取温度补偿值
float compensationValue = GetTemperatureCompensationValue(); float compensationValue = GetTemperatureCompensationValue();
Log($"获取到温度补偿值: {compensationValue}"); Log($"获取到温度补偿值: {compensationValue}");
// 创建温度数据对象,使用正确的分辨率参数 // 创建温度数据对象,使用正确的分辨率参数
TemperatureData temperatureData = new TemperatureData(temperatureFrame, WIDTH, HEIGHT, compensationValue); TemperatureData temperatureData = new TemperatureData(temperatureFrame, WIDTH, HEIGHT, compensationValue);
Log($"温度数据对象创建成功,分辨率: {WIDTH}x{HEIGHT}"); Log($"温度数据对象创建成功,分辨率: {WIDTH}x{HEIGHT}");
// 触发温度数据接收事件 // 触发温度数据接收事件
OnTemperatureReceived(new TemperatureReceivedEventArgs(temperatureData, temperatureFrame, compensationValue)); OnTemperatureReceived(new TemperatureReceivedEventArgs(temperatureData, temperatureFrame, compensationValue));
Log($"温度数据接收事件触发完成"); Log($"温度数据接收事件触发完成");
} }
Log($"温度数据处理完成"); Log($"温度数据处理完成");
} }
catch (Exception ex) catch (Exception ex)