经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Windows » 查看文章
我在winform项目里使用“Windows I/O完成端口”的经验分享
来源:cnblogs  作者:数据酷软件  时间:2024/2/23 8:59:08  对本文有异议

少年!看你骨骼惊奇,是万中无一的练武奇才,我这儿有本武林秘籍,见与你有缘就送你了! 

如来神掌

Windows I/O完成端口是一个我至今都说不好的话题,请宽容的接受我这不是科班出身的自学成才的野生程序员身份。以前在上海一公司做产品追溯的时候,我的老大拿出一本《Windows核心编程》经常向我吹嘘什么“ Windows I/O完成端口”编程模型的时候我是云里雾里。后来看了公司常用的一个叫“线程池”的类的源码,豁然有点醒悟了,不就是类似Queue这样的东西么?按先进先出顺序处理业务数据,这明明就不是线程池啊,误导人了。但是这个类确实挺好用的,公司它都使用了很多年了。不想独享特此分享出来。

  1. public class CoreThreadPool : IDisposable
  2. {
  3.         /// <summary>
  4.         /// 队列元素申明
  5.         /// </summary>
  6.         [StructLayout(LayoutKind.Sequential)]
  7. private class PoolData
  8. {
  9.             /// <summary>
  10.             /// 外部要求放入队列的数据
  11.             /// </summary>
  12.             public object Data;
  13.             /// <summary>
  14.             /// 需要执行的命令(Exit/Command(自定义))
  15.             /// </summary>
  16.             public PoolCommand Command;
  17. public PoolData()
  18. {
  19. Command = PoolCommand.Exit;
  20. }
  21. public PoolData(object data)
  22. {
  23. Data = data;
  24. Command = PoolCommand.Command;
  25. }
  26. public PoolData(PoolCommand cmd)
  27. {
  28. Command = cmd;
  29. }
  30. }
  31. protected enum PoolCommand
  32. {
  33. Command,
  34. Exit
  35. }
  36. protected SafeFileHandle complatePort;
  37.         /// <summary>
  38.         /// 线程池主线程
  39.         /// </summary>
  40.         protected Thread thread;
  41. protected volatile bool isOpened;
  42. [method: CompilerGenerated]
  43. [CompilerGenerated]
  44. public event Action<object> Exceute;
  45. [method: CompilerGenerated]
  46. [CompilerGenerated]
  47. public event Action<object> ExitExceute;
  48.         /// <summary>
  49.         /// 线程池是否正在运行
  50.         /// </summary>
  51.         public bool IsOpened
  52. {
  53. get
  54. {
  55. return this.isOpened;
  56. }
  57. set
  58. {
  59. this.isOpened = value;
  60. }
  61. }
  62. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  63. private static extern SafeFileHandle CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey, uint NumberOfConcurrentThreads);
  64. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  65. private static extern bool GetQueuedCompletionStatus(SafeFileHandle CompletionPort, out uint lpNumberOfBytesTransferred, out IntPtr lpCompletionKey, out IntPtr lpOverlapped, uint dwMilliseconds);
  66. [DllImport("Kernel32", CharSet = CharSet.Auto)]
  67. private static extern bool PostQueuedCompletionStatus(SafeFileHandle CompletionPort, uint dwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped);
  68.         /// <summary>
  69.         /// 启动线程池的主线程
  70.         /// </summary>
  71.         public void Start()
  72. {
  73. isOpened = true;
  74. if (thread != null)
  75. {
  76. throw new Exception("线程池已经是启动状态!");
  77. }
  78. complatePort = CreateIoCompletionPort(new IntPtr(-1), IntPtr.Zero, IntPtr.Zero, 0u);
  79. if (complatePort.IsInvalid)
  80. {
  81. throw new Exception(string.Format("创建IOCP出错!原因是:{0}", Marshal.GetLastWin32Error().ToString()));
  82. }
  83. thread = new Thread(new ParameterizedThreadStart(this.Run));
  84. thread.Start(complatePort);
  85. }
  86.         /// <summary>
  87.         /// 外部提交数据对象到队列
  88.         /// </summary>
  89.         /// <param name="data"></param>
  90.         public void Post(object data)
  91. {
  92. PostData(new PoolData(data));
  93. }
  94.         /// <summary>
  95.         /// 线程池主线程执行逻辑
  96.         /// </summary>
  97.         /// <param name="CompletionPortID"></param>
  98.         private void Run(object CompletionPortID)
  99. {
  100. SafeFileHandle completionPort = (SafeFileHandle)CompletionPortID;
  101. while (IsOpened)
  102. {
  103. uint num;
  104. IntPtr intPtr;
  105. IntPtr value;
  106.                 //从队列里取出最前面的对象
  107.                 GetQueuedCompletionStatus(completionPort, out num, out intPtr, out value, 4294967295u);
  108. if (num > 0u)
  109. {
  110. GCHandle gCHandle = GCHandle.FromIntPtr(value);
  111. PoolData poolData = (PoolData)gCHandle.Target;
  112. gCHandle.Free();
  113. if (poolData.Command != PoolCommand.Command)
  114. {
  115. IsOpened = false;
  116. break;
  117. }
  118. RaiseExecute(poolData.Data);
  119. }
  120. }
  121. RaiseExitExecute("线程池已经停止。");
  122. isOpened = false;
  123. thread = null;
  124. }
  125.         /// <summary>
  126.         /// 触发Execute事件
  127.         /// </summary>
  128.         /// <param name="data"></param>
  129.         private void RaiseExecute(object data)
  130. {
  131. Exceute?.Invoke(data);
  132. }
  133.         /// <summary>
  134.         /// 触发ExitExecute事件
  135.         /// </summary>
  136.         /// <param name="data"></param>
  137.         private void RaiseExitExecute(object data)
  138. {
  139. ExitExceute?.Invoke(data);
  140. }
  141.         /// <summary>
  142.         /// 结束线程池主线程
  143.         /// </summary>
  144.         public void Stop()
  145. {
  146. PostData(new PoolData(PoolCommand.Exit));
  147. IsOpened = false;
  148. }
  149.         /// <summary>
  150.         /// 内部提交数据到线程池队列中
  151.         /// </summary>
  152.         /// <param name="data"></param>
  153.         private void PostData(PoolData data)
  154. {
  155. if (complatePort.IsClosed)
  156. {
  157. return;
  158. }
  159. GCHandle value = GCHandle.Alloc(data);
  160. PostQueuedCompletionStatus(complatePort, (uint)IntPtr.Size, IntPtr.Zero, GCHandle.ToIntPtr(value));
  161. }
  162. public void Dispose()
  163. {
  164. if (thread != null && thread.ThreadState != ThreadState.Stopped)
  165. {
  166. Stop();
  167. }
  168. }
  169. }

 

第1001次实践体验过程

 

上次做的人脸考勤程序在处理多个人同时考勤时我就使用了刚刚的类。

  1. private CoreThreadPool pool = new CoreThreadPool();
  2. private CoreThreadPool poolExt = new CoreThreadPool();
  3. ...
  4. pool.Exceute += Pool_Exceute;
  5. pool.Start();
  6. poolExt.Exceute += PoolExt_Exceute;
  7. poolExt.Start()
  1. private void Pool_Exceute(object obj)
  2. {
  3. var entity = obj as UserInfo;
  4. if (entity == null) return;
  5. try
  6. {
  7. #region TODO本地防止重复请求
  8. using (DefaultDbContext db = new DefaultDbContext())
  9. {
  10. var dbEntity = db.Attenducelog.Where(e => e.Emp_No == entity.EmpNo).First();
  11. DateTime dt;
  12. if (dbEntity == null)
  13. {
  14. //第一次考勤
  15. dbEntity = new Attenducelog_Entity();
  16. dbEntity.Emp_No = entity.EmpNo;
  17. dt = DateTime.Now.AddDays(-1);
  18. dbEntity.Log_DateTime = dt;
  19. db.Attenducelog.Add(dbEntity);
  20. db.SaveChanges();
  21. }
  22. else
  23. {
  24. //已经多次考勤
  25. dt = dbEntity.Log_DateTime;
  26. }
  27. TimeSpan ts = DateTime.Now - dt;
  28. if (ts.TotalSeconds < 61)
  29. {
  30. return;
  31. }
  32. else
  33. {
  34. //已经多次考勤,本次成功了才记录打卡时间
  35. dbEntity = db.Attenducelog.Where(e => e.Emp_No == entity.EmpNo).First();
  36. dbEntity.Log_DateTime = DateTime.Now;
  37. db.Attenducelog.Update(dbEntity);
  38. db.SaveChanges();
  39. }
  40. }
  41. #endregion
  42. string url = $"{config.AppSettings.Settings["Platform"].Value}/business/attendancedetails/AddAttendanceDetails";
  43. #region dto
  44. PlatAttendanceDto dto = new PlatAttendanceDto();
  45. dto.KeyId = Guid.NewGuid().ToString();
  46. dto.Status = 0;
  47. dto.AuditDate = DateTime.Now.ToString("yyyy-MM-dd");
  48. dto.CreateBy = "AttendanceClient";
  49. dto.AttendanceDatetime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  50. dto.FkStore = config.AppSettings.Settings["StoreID"].Value;
  51. dto.EmpName = entity.Name;
  52. dto.EmpNo = entity.EmpNo;
  53. dto.WorkShift = "";
  54. dto.LocalDatetime = DateTime.Now;
  55. #endregion
  56. string jsonData = JsonConvert.SerializeObject(dto);
  57. string rs = Program.PostJsonData(url, jsonData);
  58. if (!string.IsNullOrEmpty(rs) && JObject.Parse(rs).Value<int>("code").Equals(200))
  59. {
  60. JObject rs_Object = JObject.Parse(rs);
  61. string data = rs_Object["data"].ToString();
  62. JObject log = JObject.Parse(data);
  63. string sound_TIPS = log.Value<string>("remark").Split("&".ToCharArray()).LastOrDefault();
  64. string tips = "[" + entity.Name + "] " + log.Value<string>("remark").Split("&".ToCharArray()).LastOrDefault();
  65. AppSpVoiceSpeak(sound_TIPS);
  66. MessageTip.ShowOk(tips, 3000);
  67. }
  68. }
  69. catch (Exception ex)
  70. {
  71. if (ex.Message.Contains("无法连接到远程服务器"))
  72. {
  73. Thread.Sleep(100);
  74. ViewFaceCore.Controls.MessageTip.ShowError("无法连接到远程服务器" + Environment.NewLine + "Unable to connect to remote server", 300);
  75. }
  76. }
  77. finally
  78. {
  79. Thread.Sleep(100);
  80. }
  81. }
  1. /// <summary>
  2. /// 持续检测一次人脸,直到停止。
  3. /// </summary>
  4. /// <param name="token">取消标记</param>
  5. private async void StartDetector(CancellationToken token)
  6. {
  7. List<double> fpsList = new List<double>();
  8. double fps = 0;
  9. Stopwatch stopwatchFPS = new Stopwatch();
  10. Stopwatch stopwatch = new Stopwatch();
  11. isDetecting = true;
  12. try
  13. {
  14. if (VideoPlayer == null)
  15. {
  16. return;
  17. }
  18. if (token == null)
  19. {
  20. return;
  21. }
  22. while (VideoPlayer.IsRunning && !token.IsCancellationRequested)
  23. {
  24. try
  25. {
  26. if (CheckBoxFPS.Checked)
  27. {
  28. stopwatch.Restart();
  29. if (!stopwatchFPS.IsRunning)
  30. { stopwatchFPS.Start(); }
  31. }
  32. Bitmap bitmap = VideoPlayer.GetCurrentVideoFrame(); // 获取摄像头画面
  33. if (bitmap == null)
  34. {
  35. await Task.Delay(10, token);
  36. FormHelper.SetPictureBoxImage(FacePictureBox, bitmap);
  37. continue;
  38. }
  39. if (!CheckBoxDetect.Checked)
  40. {
  41. await Task.Delay(1000 / 60, token);
  42. FormHelper.SetPictureBoxImage(FacePictureBox, bitmap);
  43. continue;
  44. }
  45. List<Models.FaceInfo> faceInfos = new List<Models.FaceInfo>();
  46. using (FaceImage faceImage = bitmap.ToFaceImage())
  47. {
  48. var infos = await faceFactory.Get<FaceTracker>().TrackAsync(faceImage);
  49. for (int i = 0; i < infos.Length; i++)
  50. {
  51. Models.FaceInfo faceInfo = new Models.FaceInfo
  52. {
  53. Pid = infos[i].Pid,
  54. Location = infos[i].Location
  55. };
  56. if (CheckBoxFaceMask.Checked || CheckBoxFaceProperty.Checked)
  57. {
  58. Model.FaceInfo info = infos[i].ToFaceInfo();
  59. if (CheckBoxFaceMask.Checked)
  60. {
  61. var maskStatus = await faceFactory.Get<MaskDetector>().PlotMaskAsync(faceImage, info);
  62. faceInfo.HasMask = maskStatus.Masked;
  63. }
  64. if (CheckBoxFaceProperty.Checked)
  65. {
  66. FaceRecognizer faceRecognizer = null;
  67. if (faceInfo.HasMask)
  68. {
  69. faceRecognizer = faceFactory.GetFaceRecognizerWithMask();
  70. }
  71. else
  72. {
  73. faceRecognizer = faceFactory.Get<FaceRecognizer>();
  74. }
  75. var points = await faceFactory.Get<FaceLandmarker>().MarkAsync(faceImage, info);
  76. float[] extractData = await faceRecognizer.ExtractAsync(faceImage, points);
  77. UserInfo userInfo = CacheManager.Instance.Get(faceRecognizer, extractData);
  78. if (userInfo != null)
  79. {
  80. faceInfo.Name = userInfo.Name;
  81. faceInfo.Age = userInfo.Age;
  82. switch (userInfo.Gender)
  83. {
  84. case GenderEnum.Male:
  85. faceInfo.Gender = Gender.Male;
  86. break;
  87. case GenderEnum.Female:
  88. faceInfo.Gender = Gender.Female;
  89. break;
  90. case GenderEnum.Unknown:
  91. faceInfo.Gender = Gender.Unknown;
  92. break;
  93. }
  94. pool.Post(userInfo);
  95. }
  96. else
  97. {
  98. faceInfo.Age = await faceFactory.Get<AgePredictor>().PredictAgeAsync(faceImage, points);
  99. faceInfo.Gender = await faceFactory.Get<GenderPredictor>().PredictGenderAsync(faceImage, points);
  100. }
  101. }
  102. }
  103. faceInfos.Add(faceInfo);
  104. }
  105. }
  106. using (Graphics g = Graphics.FromImage(bitmap))
  107. {
  108. #region 绘制当前时间
  109. StringFormat format = new StringFormat();
  110. format.Alignment = StringAlignment.Center;
  111. format.LineAlignment = StringAlignment.Center;
  112. g.DrawString($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", new Font("微软雅黑", 32), Brushes.White, new Rectangle(0, 0, Width - 32, 188), format);
  113. g.DrawString($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", new Font("微软雅黑", 32), Brushes.White, new Rectangle(2, 2, Width - 32, 188), format);
  114. #endregion
  115. // 如果有人脸,在 bitmap 上绘制出人脸的位置信息
  116. if (faceInfos.Any())
  117. {
  118. g.DrawRectangles(new Pen(Color.Red, 4), faceInfos.Select(p => p.Rectangle).ToArray());
  119. if (CheckBoxDetect.Checked)
  120. {
  121. for (int i = 0; i < faceInfos.Count; i++)
  122. {
  123. StringBuilder builder = new StringBuilder();
  124. if (CheckBoxFaceProperty.Checked)
  125. {
  126. if (!string.IsNullOrEmpty(faceInfos[i].Name))
  127. {
  128. builder.Append(faceInfos[i].Name);
  129. }
  130. }
  131. if (builder.Length > 0)
  132. {
  133. g.DrawString(builder.ToString(), new Font("微软雅黑", 32), Brushes.White, new PointF(faceInfos[i].Location.X + faceInfos[i].Location.Width + 24, faceInfos[i].Location.Y));
  134. g.DrawString(builder.ToString(), new Font("微软雅黑", 32), Brushes.White, new PointF(faceInfos[i].Location.X + faceInfos[i].Location.Width + 24 + 2, faceInfos[i].Location.Y + 2));
  135. }
  136. }
  137. }
  138. }
  139. if (CheckBoxFPS.Checked)
  140. {
  141. stopwatch.Stop();
  142. if (numericUpDownFPSTime.Value > 0)
  143. {
  144. fpsList.Add(1000f / stopwatch.ElapsedMilliseconds);
  145. if (stopwatchFPS.ElapsedMilliseconds >= numericUpDownFPSTime.Value)
  146. {
  147. fps = fpsList.Average();
  148. fpsList.Clear();
  149. stopwatchFPS.Reset();
  150. }
  151. }
  152. else
  153. {
  154. fps = 1000f / stopwatch.ElapsedMilliseconds;
  155. }
  156. g.DrawString($"{fps:#.#} FPS", new Font("微软雅黑", 24), Brushes.Green, new Point(10, 10));
  157. }
  158. }
  159. FormHelper.SetPictureBoxImage(FacePictureBox, bitmap);
  160. }
  161. catch (TaskCanceledException)
  162. {
  163. break;
  164. }
  165. catch { }
  166. }
  167. }
  168. catch (Exception ex)
  169. {
  170. Program.AppLogger.Error(ex);
  171. }
  172. finally
  173. {
  174. isDetecting = false;
  175. }
  176. }

其实触发数据就一句代码,看起来像这样:pool.Post(userInfo);

好了,高手请看笑话吃瓜,有需要的同学可亲自尝试。bye 了个 bye!

原文链接:https://www.cnblogs.com/datacool/p/18027003/CoolThearPool

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号