我在winform项目里使用“Windows I/O完成端口”的经验分享
少年!看你骨骼惊奇,是万中无一的练武奇才,我这儿有本武林秘籍,见与你有缘就送你了!
如来神掌
Windows I/O完成端口是一个我至今都说不好的话题,请宽容的接受我这不是科班出身的自学成才的野生程序员身份。以前在上海一公司做产品追溯的时候,我的老大拿出一本《Windows核心编程》经常向我吹嘘什么“ Windows I/O完成端口”编程模型的时候我是云里雾里。后来看了公司常用的一个叫“线程池”的类的源码,豁然有点醒悟了,不就是类似Queue这样的东西么?按先进先出顺序处理业务数据,这明明就不是线程池啊,误导人了。但是这个类确实挺好用的,公司它都使用了很多年了。不想独享特此分享出来。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169publicclassCoreThreadPool : IDisposable{/// <summary>/// 队列元素申明/// </summary>[StructLayout(LayoutKind.Sequential)]privateclassPoolData{/// <summary>/// 外部要求放入队列的数据/// </summary>publicobjectData;/// <summary>/// 需要执行的命令(Exit/Command(自定义))/// </summary>publicPoolCommand Command;publicPoolData(){Command = PoolCommand.Exit;}publicPoolData(objectdata){Data = data;Command = PoolCommand.Command;}publicPoolData(PoolCommand cmd){Command = cmd;}}protectedenumPoolCommand{Command,Exit}protectedSafeFileHandle complatePort;/// <summary>/// 线程池主线程/// </summary>protectedThread thread;protectedvolatileboolisOpened;[method: CompilerGenerated][CompilerGenerated]publiceventAction<object> Exceute;[method: CompilerGenerated][CompilerGenerated]publiceventAction<object> ExitExceute;/// <summary>/// 线程池是否正在运行/// </summary>publicboolIsOpened{get{returnthis.isOpened;}set{this.isOpened = value;}}[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError =true)]privatestaticexternSafeFileHandle CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey,uintNumberOfConcurrentThreads);[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError =true)]privatestaticexternboolGetQueuedCompletionStatus(SafeFileHandle CompletionPort,outuintlpNumberOfBytesTransferred,outIntPtr lpCompletionKey,outIntPtr lpOverlapped,uintdwMilliseconds);[DllImport("Kernel32", CharSet = CharSet.Auto)]privatestaticexternboolPostQueuedCompletionStatus(SafeFileHandle CompletionPort,uintdwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped);/// <summary>/// 启动线程池的主线程/// </summary>publicvoidStart(){isOpened =true;if(thread !=null){thrownewException("线程池已经是启动状态!");}complatePort = CreateIoCompletionPort(newIntPtr(-1), IntPtr.Zero, IntPtr.Zero, 0u);if(complatePort.IsInvalid){thrownewException(string.Format("创建IOCP出错!原因是:{0}", Marshal.GetLastWin32Error().ToString()));}thread =newThread(newParameterizedThreadStart(this.Run));thread.Start(complatePort);}/// <summary>/// 外部提交数据对象到队列/// </summary>/// <param name="data"></param>publicvoidPost(objectdata){PostData(newPoolData(data));}/// <summary>/// 线程池主线程执行逻辑/// </summary>/// <param name="CompletionPortID"></param>privatevoidRun(objectCompletionPortID){SafeFileHandle completionPort = (SafeFileHandle)CompletionPortID;while(IsOpened){uintnum;IntPtr intPtr;IntPtr value;//从队列里取出最前面的对象GetQueuedCompletionStatus(completionPort,outnum,outintPtr,outvalue, 4294967295u);if(num > 0u){GCHandle gCHandle = GCHandle.FromIntPtr(value);PoolData poolData = (PoolData)gCHandle.Target;gCHandle.Free();if(poolData.Command != PoolCommand.Command){IsOpened =false;break;}RaiseExecute(poolData.Data);}}RaiseExitExecute("线程池已经停止。");isOpened =false;thread =null;}/// <summary>/// 触发Execute事件/// </summary>/// <param name="data"></param>privatevoidRaiseExecute(objectdata){Exceute?.Invoke(data);}/// <summary>/// 触发ExitExecute事件/// </summary>/// <param name="data"></param>privatevoidRaiseExitExecute(objectdata){ExitExceute?.Invoke(data);}/// <summary>/// 结束线程池主线程/// </summary>publicvoidStop(){PostData(newPoolData(PoolCommand.Exit));IsOpened =false;}/// <summary>/// 内部提交数据到线程池队列中/// </summary>/// <param name="data"></param>privatevoidPostData(PoolData data){if(complatePort.IsClosed){return;}GCHandle value = GCHandle.Alloc(data);PostQueuedCompletionStatus(complatePort, (uint)IntPtr.Size, IntPtr.Zero, GCHandle.ToIntPtr(value));}publicvoidDispose(){if(thread !=null&& thread.ThreadState != ThreadState.Stopped){Stop();}}}
第1001次实践体验过程

上次做的人脸考勤程序在处理多个人同时考勤时我就使用了刚刚的类。
private CoreThreadPool pool = new CoreThreadPool(); private CoreThreadPool poolExt = new CoreThreadPool(); ... pool.Exceute += Pool_Exceute; pool.Start(); poolExt.Exceute += PoolExt_Exceute; poolExt.Start()
private void Pool_Exceute(object obj)
{
var entity = obj as UserInfo;
if (entity == null) return;
try
{
#region TODO本地防止重复请求
using (DefaultDbContext db = new DefaultDbContext())
{
var dbEntity = db.Attenducelog.Where(e => e.Emp_No == entity.EmpNo).First();
DateTime dt;
if (dbEntity == null)
{
//第一次考勤
dbEntity = new Attenducelog_Entity();
dbEntity.Emp_No = entity.EmpNo;
dt = DateTime.Now.AddDays(-1);
dbEntity.Log_DateTime = dt;
db.Attenducelog.Add(dbEntity);
db.SaveChanges();
}
else
{
//已经多次考勤
dt = dbEntity.Log_DateTime;
}
TimeSpan ts = DateTime.Now - dt;
if (ts.TotalSeconds < 61)
{
return;
}
else
{
//已经多次考勤,本次成功了才记录打卡时间
dbEntity = db.Attenducelog.Where(e => e.Emp_No == entity.EmpNo).First();
dbEntity.Log_DateTime = DateTime.Now;
db.Attenducelog.Update(dbEntity);
db.SaveChanges();
}
}
#endregion
string url = $"{config.AppSettings.Settings["Platform"].Value}/business/attendancedetails/AddAttendanceDetails";
#region dto
PlatAttendanceDto dto = new PlatAttendanceDto();
dto.KeyId = Guid.NewGuid().ToString();
dto.Status = 0;
dto.AuditDate = DateTime.Now.ToString("yyyy-MM-dd");
dto.CreateBy = "AttendanceClient";
dto.AttendanceDatetime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
dto.FkStore = config.AppSettings.Settings["StoreID"].Value;
dto.EmpName = entity.Name;
dto.EmpNo = entity.EmpNo;
dto.WorkShift = "";
dto.LocalDatetime = DateTime.Now;
#endregion
string jsonData = JsonConvert.SerializeObject(dto);
string rs = Program.PostJsonData(url, jsonData);
if (!string.IsNullOrEmpty(rs) && JObject.Parse(rs).Value<int>("code").Equals(200))
{
JObject rs_Object = JObject.Parse(rs);
string data = rs_Object["data"].ToString();
JObject log = JObject.Parse(data);
string sound_TIPS = log.Value<string>("remark").Split("&".ToCharArray()).LastOrDefault();
string tips = "[" + entity.Name + "] " + log.Value<string>("remark").Split("&".ToCharArray()).LastOrDefault();
AppSpVoiceSpeak(sound_TIPS);
MessageTip.ShowOk(tips, 3000);
}
}
catch (Exception ex)
{
if (ex.Message.Contains("无法连接到远程服务器"))
{
Thread.Sleep(100);
ViewFaceCore.Controls.MessageTip.ShowError("无法连接到远程服务器" + Environment.NewLine + "Unable to connect to remote server", 300);
}
}
finally
{
Thread.Sleep(100);
}
}
/// <summary>
/// 持续检测一次人脸,直到停止。
/// </summary>
/// <param name="token">取消标记</param>
private async void StartDetector(CancellationToken token)
{
List<double> fpsList = new List<double>();
double fps = 0;
Stopwatch stopwatchFPS = new Stopwatch();
Stopwatch stopwatch = new Stopwatch();
isDetecting = true;
try
{
if (VideoPlayer == null)
{
return;
}
if (token == null)
{
return;
}
while (VideoPlayer.IsRunning && !token.IsCancellationRequested)
{
try
{
if (CheckBoxFPS.Checked)
{
stopwatch.Restart();
if (!stopwatchFPS.IsRunning)
{ stopwatchFPS.Start(); }
}
Bitmap bitmap = VideoPlayer.GetCurrentVideoFrame(); // 获取摄像头画面
if (bitmap == null)
{
await Task.Delay(10, token);
FormHelper.SetPictureBoxImage(FacePictureBox, bitmap);
continue;
}
if (!CheckBoxDetect.Checked)
{
await Task.Delay(1000 / 60, token);
FormHelper.SetPictureBoxImage(FacePictureBox, bitmap);
continue;
}
List<Models.FaceInfo> faceInfos = new List<Models.FaceInfo>();
using (FaceImage faceImage = bitmap.ToFaceImage())
{
var infos = await faceFactory.Get<FaceTracker>().TrackAsync(faceImage);
for (int i = 0; i < infos.Length; i++)
{
Models.FaceInfo faceInfo = new Models.FaceInfo
{
Pid = infos[i].Pid,
Location = infos[i].Location
};
if (CheckBoxFaceMask.Checked || CheckBoxFaceProperty.Checked)
{
Model.FaceInfo info = infos[i].ToFaceInfo();
if (CheckBoxFaceMask.Checked)
{
var maskStatus = await faceFactory.Get<MaskDetector>().PlotMaskAsync(faceImage, info);
faceInfo.HasMask = maskStatus.Masked;
}
if (CheckBoxFaceProperty.Checked)
{
FaceRecognizer faceRecognizer = null;
if (faceInfo.HasMask)
{
faceRecognizer = faceFactory.GetFaceRecognizerWithMask();
}
else
{
faceRecognizer = faceFactory.Get<FaceRecognizer>();
}
var points = await faceFactory.Get<FaceLandmarker>().MarkAsync(faceImage, info);
float[] extractData = await faceRecognizer.ExtractAsync(faceImage, points);
UserInfo userInfo = CacheManager.Instance.Get(faceRecognizer, extractData);
if (userInfo != null)
{
faceInfo.Name = userInfo.Name;
faceInfo.Age = userInfo.Age;
switch (userInfo.Gender)
{
case GenderEnum.Male:
faceInfo.Gender = Gender.Male;
break;
case GenderEnum.Female:
faceInfo.Gender = Gender.Female;
break;
case GenderEnum.Unknown:
faceInfo.Gender = Gender.Unknown;
break;
}
pool.Post(userInfo);
}
else
{
faceInfo.Age = await faceFactory.Get<AgePredictor>().PredictAgeAsync(faceImage, points);
faceInfo.Gender = await faceFactory.Get<GenderPredictor>().PredictGenderAsync(faceImage, points);
}
}
}
faceInfos.Add(faceInfo);
}
}
using (Graphics g = Graphics.FromImage(bitmap))
{
#region 绘制当前时间
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
g.DrawString($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", new Font("微软雅黑", 32), Brushes.White, new Rectangle(0, 0, Width - 32, 188), format);
g.DrawString($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", new Font("微软雅黑", 32), Brushes.White, new Rectangle(2, 2, Width - 32, 188), format);
#endregion
// 如果有人脸,在 bitmap 上绘制出人脸的位置信息
if (faceInfos.Any())
{
g.DrawRectangles(new Pen(Color.Red, 4), faceInfos.Select(p => p.Rectangle).ToArray());
if (CheckBoxDetect.Checked)
{
for (int i = 0; i < faceInfos.Count; i++)
{
StringBuilder builder = new StringBuilder();
if (CheckBoxFaceProperty.Checked)
{
if (!string.IsNullOrEmpty(faceInfos[i].Name))
{
builder.Append(faceInfos[i].Name);
}
}
if (builder.Length > 0)
{
g.DrawString(builder.ToString(), new Font("微软雅黑", 32), Brushes.White, new PointF(faceInfos[i].Location.X + faceInfos[i].Location.Width + 24, faceInfos[i].Location.Y));
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));
}
}
}
}
if (CheckBoxFPS.Checked)
{
stopwatch.Stop();
if (numericUpDownFPSTime.Value > 0)
{
fpsList.Add(1000f / stopwatch.ElapsedMilliseconds);
if (stopwatchFPS.ElapsedMilliseconds >= numericUpDownFPSTime.Value)
{
fps = fpsList.Average();
fpsList.Clear();
stopwatchFPS.Reset();
}
}
else
{
fps = 1000f / stopwatch.ElapsedMilliseconds;
}
g.DrawString($"{fps:#.#} FPS", new Font("微软雅黑", 24), Brushes.Green, new Point(10, 10));
}
}
FormHelper.SetPictureBoxImage(FacePictureBox, bitmap);
}
catch (TaskCanceledException)
{
break;
}
catch { }
}
}
catch (Exception ex)
{
Program.AppLogger.Error(ex);
}
finally
{
isDetecting = false;
}
}
其实触发数据就一句代码,看起来像这样:pool.Post(userInfo);
好了,高手请看笑话吃瓜,有需要的同学可亲自尝试。bye 了个 bye!
作者:数据酷软件
出处:https://www.cnblogs.com/datacool/p/18027003/CoolThearPool
关于作者:20年编程从业经验,持续关注MES/ERP/POS/WMS/工业自动化
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明。
联系方式: qq:71008973;wx:6857740733
基于人脸识别的考勤系统 地址: https://gitee.com/afeng124/viewface_attendance_ext
自己开发安卓应用框架 地址: https://gitee.com/afeng124/android-app-frame
WPOS(warehouse+pos) 后台演示地址: http://47.239.106.75:8080/


浙公网安备 33010602011771号