ThreadManager.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. namespace ThreadManagerTest
  9. {
  10. public class ThreadManager
  11. {
  12. private readonly CameraGroup[] _cameraGroups = new CameraGroup[4];
  13. private CancellationTokenSource _communicationCts;
  14. private CancellationTokenSource _displayCts;
  15. private Task _communicationTask;
  16. private Task _displayTask;
  17. private bool _isCommunicationRunning = false;
  18. private bool _isDisplayRunning = false;
  19. // 用于存储待发送的结果数据的队列
  20. private readonly ConcurrentQueue<ProcessResultEventArgs> _resultQueue = new ConcurrentQueue<ProcessResultEventArgs>();
  21. private readonly SemaphoreSlim _resultSemaphore = new SemaphoreSlim(0);
  22. public ThreadManager()
  23. {
  24. // 初始化四个相机组
  25. for (int i = 0; i < 4; i++)
  26. {
  27. _cameraGroups[i] = new CameraGroup
  28. {
  29. CameraId = i,
  30. ImageQueue = new ConcurrentQueue<ImageData>(),
  31. QueueSemaphore = new SemaphoreSlim(0),
  32. CancellationTokenSource = new CancellationTokenSource()
  33. };
  34. // 订阅处理结果事件
  35. _cameraGroups[i].ProcessResultAvailable += OnProcessResultAvailable;
  36. }
  37. }
  38. /// <summary>
  39. /// 处理结果事件处理器
  40. /// 当任一相机组产生处理结果时都会调用此方法
  41. /// </summary>
  42. private void OnProcessResultAvailable(object sender, ProcessResultEventArgs e)
  43. {
  44. // 将结果数据加入队列
  45. _resultQueue.Enqueue(e);
  46. // 通知通信线程有新数据
  47. _resultSemaphore.Release();
  48. }
  49. /// <summary>
  50. /// 通信线程循环 - 修改版
  51. /// </summary>
  52. private async void CommunicationLoop(CancellationToken token)
  53. {
  54. try
  55. {
  56. while (!token.IsCancellationRequested)
  57. {
  58. // 等待结果数据
  59. await _resultSemaphore.WaitAsync(token);
  60. // 检查是否有待发送的结果
  61. if (_resultQueue.TryDequeue(out ProcessResultEventArgs result))
  62. {
  63. // 发送结果数据
  64. SendResultToCommunicationDevice(result);
  65. }
  66. // 1ms休眠
  67. await Task.Delay(1, token);
  68. }
  69. }
  70. catch (OperationCanceledException)
  71. {
  72. // 线程被取消,正常退出
  73. }
  74. catch (Exception ex)
  75. {
  76. Console.WriteLine($"通信线程异常: {ex.Message}");
  77. }
  78. }
  79. /// <summary>
  80. /// 发送结果数据到通信设备
  81. /// </summary>
  82. private void SendResultToCommunicationDevice(ProcessResultEventArgs result)
  83. {
  84. // 实际的通信发送逻辑
  85. Console.WriteLine($"发送相机{result.CameraId}的结果数据: " +
  86. $"目标数={((ProcessResultData)result.ResultData).TargetCount}, " +
  87. $"时间={result.Timestamp}");
  88. // 这里实现实际的串口、网络或其他通信协议发送逻辑
  89. }
  90. /// <summary>
  91. /// 启动指定相机组(包括采集和处理线程)
  92. /// </summary>
  93. /// <param name="cameraId">相机ID (0-3)</param>
  94. public void StartCameraGroup(int cameraId)
  95. {
  96. if (cameraId < 0 || cameraId >= 4)
  97. throw new ArgumentException("相机ID必须在0-3之间");
  98. var group = _cameraGroups[cameraId];
  99. if (group.IsRunning)
  100. return;
  101. // 重置取消令牌
  102. group.CancellationTokenSource = new CancellationTokenSource();
  103. var token = group.CancellationTokenSource.Token;
  104. // 启动相机采集线程
  105. group.CameraTask = Task.Factory.StartNew(() => CameraCaptureLoop(group, token),
  106. token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
  107. // 启动图像处理线程
  108. group.ProcessingTask = Task.Factory.StartNew(() => ImageProcessingLoop(group, token),
  109. token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
  110. group.IsRunning = true;
  111. Console.WriteLine($"相机组 {cameraId} 已启动");
  112. }
  113. /// <summary>
  114. /// 停止指定相机组
  115. /// </summary>
  116. /// <param name="cameraId">相机ID (0-3)</param>
  117. public async void StopCameraGroup(int cameraId)
  118. {
  119. if (cameraId < 0 || cameraId >= 4)
  120. throw new ArgumentException("相机ID必须在0-3之间");
  121. var group = _cameraGroups[cameraId];
  122. if (!group.IsRunning)
  123. return;
  124. // 发送取消信号
  125. group.CancellationTokenSource.Cancel();
  126. // 等待线程完成
  127. try
  128. {
  129. if (group.CameraTask != null)
  130. await group.CameraTask;
  131. if (group.ProcessingTask != null)
  132. await group.ProcessingTask;
  133. }
  134. catch (OperationCanceledException)
  135. {
  136. // 正常取消,忽略异常
  137. }
  138. group.IsRunning = false;
  139. Console.WriteLine($"相机组 {cameraId} 已停止");
  140. }
  141. /// <summary>
  142. /// 启动通信线程
  143. /// </summary>
  144. public void StartCommunicationThread()
  145. {
  146. if (_isCommunicationRunning)
  147. return;
  148. _communicationCts = new CancellationTokenSource();
  149. var token = _communicationCts.Token;
  150. _communicationTask = Task.Factory.StartNew(() => CommunicationLoop(token),
  151. token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
  152. _isCommunicationRunning = true;
  153. Console.WriteLine("通信线程已启动");
  154. }
  155. /// <summary>
  156. /// 停止通信线程
  157. /// </summary>
  158. public async void StopCommunicationThread()
  159. {
  160. if (!_isCommunicationRunning)
  161. return;
  162. _communicationCts.Cancel();
  163. try
  164. {
  165. if (_communicationTask != null)
  166. await _communicationTask;
  167. }
  168. catch (OperationCanceledException)
  169. {
  170. // 正常取消,忽略异常
  171. }
  172. _isCommunicationRunning = false;
  173. Console.WriteLine("通信线程已停止");
  174. }
  175. /// <summary>
  176. /// 启动显示线程
  177. /// </summary>
  178. public void StartDisplayThread()
  179. {
  180. if (_isDisplayRunning)
  181. return;
  182. _displayCts = new CancellationTokenSource();
  183. var token = _displayCts.Token;
  184. _displayTask = Task.Factory.StartNew(() => DisplayLoop(token),
  185. token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
  186. _isDisplayRunning = true;
  187. Console.WriteLine("显示线程已启动");
  188. }
  189. /// <summary>
  190. /// 停止显示线程
  191. /// </summary>
  192. public async void StopDisplayThread()
  193. {
  194. if (!_isDisplayRunning)
  195. return;
  196. _displayCts.Cancel();
  197. try
  198. {
  199. if (_displayTask != null)
  200. await _displayTask;
  201. }
  202. catch (OperationCanceledException)
  203. {
  204. // 正常取消,忽略异常
  205. }
  206. _isDisplayRunning = false;
  207. Console.WriteLine("显示线程已停止");
  208. }
  209. /// <summary>
  210. /// 启动所有线程组
  211. /// </summary>
  212. public void StartAll()
  213. {
  214. // 启动所有相机组
  215. for (int i = 0; i < 4; i++)
  216. {
  217. StartCameraGroup(i);
  218. }
  219. // 启动通信和显示线程
  220. StartCommunicationThread();
  221. StartDisplayThread();
  222. }
  223. /// <summary>
  224. /// 停止所有线程组
  225. /// </summary>
  226. public void StopAll()
  227. {
  228. // 停止所有相机组
  229. for (int i = 0; i < 4; i++)
  230. {
  231. StopCameraGroup(i);
  232. }
  233. // 停止通信和显示线程
  234. StopCommunicationThread();
  235. StopDisplayThread();
  236. }
  237. /// <summary>
  238. /// 检查指定相机组是否正在运行
  239. /// </summary>
  240. /// <param name="cameraId">相机ID</param>
  241. /// <returns>运行状态</returns>
  242. public bool IsCameraGroupRunning(int cameraId)
  243. {
  244. if (cameraId < 0 || cameraId >= 4)
  245. return false;
  246. return _cameraGroups[cameraId].IsRunning;
  247. }
  248. /// <summary>
  249. /// 检查通信线程是否正在运行
  250. /// </summary>
  251. public bool IsCommunicationRunning => _isCommunicationRunning;
  252. /// <summary>
  253. /// 检查显示线程是否正在运行
  254. /// </summary>
  255. public bool IsDisplayRunning => _isDisplayRunning;
  256. #region 线程执行逻辑
  257. /// <summary>
  258. /// 相机采集循环
  259. /// </summary>
  260. private void CameraCaptureLoop(CameraGroup group, CancellationToken token)
  261. {
  262. int frameCount = 0;
  263. try
  264. {
  265. while (!token.IsCancellationRequested)
  266. {
  267. // 模拟相机图像采集(无休眠,持续采集)
  268. var imageData = new ImageData
  269. {
  270. CameraId = group.CameraId,
  271. RawData = new byte[1024], // 模拟图像数据
  272. Timestamp = DateTime.Now,
  273. FrameNumber = frameCount++
  274. };
  275. // 将图像放入队列
  276. group.ImageQueue.Enqueue(imageData);
  277. group.QueueSemaphore.Release(); // 通知处理线程有新数据
  278. // 这里不添加休眠,保持最大帧率采集
  279. }
  280. }
  281. catch (OperationCanceledException)
  282. {
  283. // 线程被取消,正常退出
  284. }
  285. catch (Exception ex)
  286. {
  287. Console.WriteLine($"相机 {group.CameraId} 采集异常: {ex.Message}");
  288. }
  289. }
  290. /// <summary>
  291. /// 图像处理循环
  292. /// </summary>
  293. private async void ImageProcessingLoop(CameraGroup group, CancellationToken token)
  294. {
  295. try
  296. {
  297. while (!token.IsCancellationRequested)
  298. {
  299. // 等待图像数据
  300. await group.QueueSemaphore.WaitAsync(token);
  301. // 从队列获取图像
  302. if (group.ImageQueue.TryDequeue(out ImageData imageData))
  303. {
  304. // 执行图像处理逻辑
  305. ProcessImage(imageData,group);
  306. // 可以根据队列长度决定是否短暂休眠以避免过度占用CPU
  307. if (group.ImageQueue.Count == 0)
  308. {
  309. await Task.Delay(1, token); // 短暂让出CPU
  310. }
  311. }
  312. }
  313. }
  314. catch (OperationCanceledException)
  315. {
  316. // 线程被取消,正常退出
  317. }
  318. catch (Exception ex)
  319. {
  320. Console.WriteLine($"相机 {group.CameraId} 处理异常: {ex.Message}");
  321. }
  322. }
  323. /// <summary>
  324. /// 图像处理实现 - 修改版
  325. /// </summary>
  326. private void ProcessImage(ImageData imageData, CameraGroup group)
  327. {
  328. var stopwatch = System.Diagnostics.Stopwatch.StartNew();
  329. // 执行图像处理逻辑
  330. var result = PerformImageAnalysis(imageData);
  331. stopwatch.Stop();
  332. // 创建处理结果数据
  333. var resultData = new ProcessResultData
  334. {
  335. TargetCount = result.Targets.Count,
  336. TargetPositions = result.Targets.Select(t => t.Position).ToList(),
  337. ProcessingTimeMs = stopwatch.ElapsedMilliseconds,
  338. FrameNumber = imageData.FrameNumber,
  339. ConfidenceScore = result.AverageConfidence
  340. };
  341. // 发送处理结果
  342. group.SendProcessResult(resultData);
  343. Console.WriteLine($"相机 {imageData.CameraId} 处理帧 {imageData.FrameNumber},发现 {result.Targets.Count} 个目标");
  344. }
  345. /// <summary>
  346. /// 图像分析模拟方法
  347. /// </summary>
  348. private ImageAnalysisResult PerformImageAnalysis(ImageData imageData)
  349. {
  350. // 模拟图像分析过程
  351. Thread.Sleep(10); // 模拟处理时间
  352. // 模拟分析结果
  353. return new ImageAnalysisResult
  354. {
  355. Targets = new List<TargetInfo>
  356. {
  357. new TargetInfo
  358. {
  359. Position = new Point { X = 100, Y = 150 },
  360. Confidence = 0.95
  361. },
  362. new TargetInfo
  363. {
  364. Position = new Point { X = 200, Y = 300 },
  365. Confidence = 0.87
  366. }
  367. },
  368. AverageConfidence = 0.91
  369. };
  370. }
  371. /// <summary>
  372. /// 显示线程循环
  373. /// </summary>
  374. private async void DisplayLoop(CancellationToken token)
  375. {
  376. var stopwatch = System.Diagnostics.Stopwatch.StartNew();
  377. long targetFrameTime = 1000 / 24; // 24fps对应的时间间隔(ms)
  378. try
  379. {
  380. while (!token.IsCancellationRequested)
  381. {
  382. stopwatch.Restart();
  383. // 执行显示逻辑
  384. UpdateDisplay();
  385. stopwatch.Stop();
  386. long elapsed = stopwatch.ElapsedMilliseconds;
  387. // 动态调整休眠时间以维持24fps
  388. long sleepTime = Math.Max(0, targetFrameTime - elapsed);
  389. if (sleepTime > 0)
  390. {
  391. await Task.Delay((int)sleepTime, token);
  392. }
  393. }
  394. }
  395. catch (OperationCanceledException)
  396. {
  397. // 线程被取消,正常退出
  398. }
  399. catch (Exception ex)
  400. {
  401. Console.WriteLine($"显示线程异常: {ex.Message}");
  402. }
  403. }
  404. /// <summary>
  405. /// 显示更新实现
  406. /// </summary>
  407. private void UpdateDisplay()
  408. {
  409. // 实现显示更新逻辑
  410. Console.WriteLine("更新显示画面");
  411. }
  412. #endregion
  413. }
  414. /// <summary>
  415. /// 相机组配置和状态管理类
  416. /// 管理单个相机的采集和处理线程及相关资源
  417. /// </summary>
  418. public class CameraGroup
  419. {
  420. public int CameraId { get; set; }
  421. public bool IsRunning { get; set; }
  422. public CancellationTokenSource CancellationTokenSource { get; set; }
  423. public Task CameraTask { get; set; }
  424. public Task ProcessingTask { get; set; }
  425. public ConcurrentQueue<ImageData> ImageQueue { get; set; }
  426. public SemaphoreSlim QueueSemaphore { get; set; }
  427. // 结果发送事件,当图像处理完成时触发
  428. public event EventHandler<ProcessResultEventArgs> ProcessResultAvailable;
  429. /// <summary>
  430. /// 发送处理结果到通信线程的方法
  431. /// 多个线程可以共用此方法
  432. /// </summary>
  433. /// <param name="resultData">处理结果数据</param>
  434. public void SendProcessResult(object resultData)
  435. {
  436. var eventArgs = new ProcessResultEventArgs
  437. {
  438. CameraId = this.CameraId,
  439. ResultData = resultData,
  440. Timestamp = DateTime.Now
  441. };
  442. // 触发事件,通知订阅者有新的处理结果
  443. ProcessResultAvailable?.Invoke(this, eventArgs);
  444. }
  445. }
  446. /// <summary>
  447. /// 处理结果事件参数类
  448. /// 包含相机ID和处理结果数据
  449. /// </summary>
  450. public class ProcessResultEventArgs : EventArgs
  451. {
  452. /// <summary>
  453. /// 相机ID
  454. /// </summary>
  455. public int CameraId { get; set; }
  456. /// <summary>
  457. /// 处理结果数据
  458. /// </summary>
  459. public object ResultData { get; set; }
  460. /// <summary>
  461. /// 结果生成时间戳
  462. /// </summary>
  463. public DateTime Timestamp { get; set; }
  464. }
  465. /// <summary>
  466. /// 处理结果数据结构示例
  467. /// </summary>
  468. public class ProcessResultData
  469. {
  470. /// <summary>
  471. /// 检测到的目标数量
  472. /// </summary>
  473. public int TargetCount { get; set; }
  474. /// <summary>
  475. /// 目标位置坐标列表
  476. /// </summary>
  477. public List<Point> TargetPositions { get; set; }
  478. /// <summary>
  479. /// 处理耗时(毫秒)
  480. /// </summary>
  481. public long ProcessingTimeMs { get; set; }
  482. /// <summary>
  483. /// 图像帧编号
  484. /// </summary>
  485. public int FrameNumber { get; set; }
  486. /// <summary>
  487. /// 置信度评分
  488. /// </summary>
  489. public double ConfidenceScore { get; set; }
  490. }
  491. /// <summary>
  492. /// 点坐标结构
  493. /// </summary>
  494. public struct Point
  495. {
  496. public float X { get; set; }
  497. public float Y { get; set; }
  498. }
  499. /// <summary>
  500. /// 图像数据结构
  501. /// </summary>
  502. public class ImageData
  503. {
  504. public int CameraId { get; set; }
  505. public byte[] RawData { get; set; }
  506. public DateTime Timestamp { get; set; }
  507. public int FrameNumber { get; set; }
  508. }
  509. /// <summary>
  510. /// 图像分析结果类
  511. /// </summary>
  512. public class ImageAnalysisResult
  513. {
  514. public List<TargetInfo> Targets { get; set; }
  515. public double AverageConfidence { get; set; }
  516. }
  517. /// <summary>
  518. /// 目标信息类
  519. /// </summary>
  520. public class TargetInfo
  521. {
  522. public Point Position { get; set; }
  523. public double Confidence { get; set; }
  524. }
  525. }