我在winform项目里使用“Windows I/O完成端口”的经验分享
1.贡献个Winform自定义控件,信用卡、银行卡输入控件;每4个字符分隔显示。其他UI框架可以参考。2.印章WinForm自定义控件封装,提供源码下载3.Windows高速定时器,多媒体定时器winmm.dll库的使用4.理解SynchronizationContext,如何在Winform里面跨线程访问UI控件5.C#winform程序关闭计算机的正确姿势6.WinForm触摸屏程序功能界面长时间不操作自动关闭回到主界面7.分享Winform datagridview 动态生成中文HeaderText8.Winform让扫描枪听话,防止在有焦点的地方就扫码输入的尴尬9.WinForm调用钉钉获取考勤结果10.C# Winform 实现Ajax效果自定义按钮11.C#Winform 自定义透明按钮和单窗体模块化实现12.C#Winform设计的通用标签设计器13.C#Winform使用mysql作为本地数据库14.C#winform自定义控件模拟设计时界面鼠标移动和调节大小、选中效果
15.我在winform项目里使用“Windows I/O完成端口”的经验分享
16.一次人脸识别ViewFaceCore使用的经验分享,看我把门店淘汰下来的POS机改成了人脸考勤机少年!看你骨骼惊奇,是万中无一的练武奇才,我这儿有本武林秘籍,见与你有缘就送你了!
如来神掌
Windows I/O完成端口是一个我至今都说不好的话题,请宽容的接受我这不是科班出身的自学成才的野生程序员身份。以前在上海一公司做产品追溯的时候,我的老大拿出一本《Windows核心编程》经常向我吹嘘什么“ Windows I/O完成端口”编程模型的时候我是云里雾里。后来看了公司常用的一个叫“线程池”的类的源码,豁然有点醒悟了,不就是类似Queue这样的东西么?按先进先出顺序处理业务数据,这明明就不是线程池啊,误导人了。但是这个类确实挺好用的,公司它都使用了很多年了。不想独享特此分享出来。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
public
class
CoreThreadPool : IDisposable
{
/// <summary>
/// 队列元素申明
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private
class
PoolData
{
/// <summary>
/// 外部要求放入队列的数据
/// </summary>
public
object
Data;
/// <summary>
/// 需要执行的命令(Exit/Command(自定义))
/// </summary>
public
PoolCommand Command;
public
PoolData()
{
Command = PoolCommand.Exit;
}
public
PoolData(
object
data)
{
Data = data;
Command = PoolCommand.Command;
}
public
PoolData(PoolCommand cmd)
{
Command = cmd;
}
}
protected
enum
PoolCommand
{
Command,
Exit
}
protected
SafeFileHandle complatePort;
/// <summary>
/// 线程池主线程
/// </summary>
protected
Thread thread;
protected
volatile
bool
isOpened;
[method: CompilerGenerated]
[CompilerGenerated]
public
event
Action<
object
> Exceute;
[method: CompilerGenerated]
[CompilerGenerated]
public
event
Action<
object
> ExitExceute;
/// <summary>
/// 线程池是否正在运行
/// </summary>
public
bool
IsOpened
{
get
{
return
this
.isOpened;
}
set
{
this
.isOpened = value;
}
}
[DllImport(
"kernel32.dll"
, CharSet = CharSet.Auto, SetLastError =
true
)]
private
static
extern
SafeFileHandle CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey,
uint
NumberOfConcurrentThreads);
[DllImport(
"kernel32.dll"
, CharSet = CharSet.Auto, SetLastError =
true
)]
private
static
extern
bool
GetQueuedCompletionStatus(SafeFileHandle CompletionPort,
out
uint
lpNumberOfBytesTransferred,
out
IntPtr lpCompletionKey,
out
IntPtr lpOverlapped,
uint
dwMilliseconds);
[DllImport(
"Kernel32"
, CharSet = CharSet.Auto)]
private
static
extern
bool
PostQueuedCompletionStatus(SafeFileHandle CompletionPort,
uint
dwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped);
/// <summary>
/// 启动线程池的主线程
/// </summary>
public
void
Start()
{
isOpened =
true
;
if
(thread !=
null
)
{
throw
new
Exception(
"线程池已经是启动状态!"
);
}
complatePort = CreateIoCompletionPort(
new
IntPtr(-1), IntPtr.Zero, IntPtr.Zero, 0u);
if
(complatePort.IsInvalid)
{
throw
new
Exception(
string
.Format(
"创建IOCP出错!原因是:{0}"
, Marshal.GetLastWin32Error().ToString()));
}
thread =
new
Thread(
new
ParameterizedThreadStart(
this
.Run));
thread.Start(complatePort);
}
/// <summary>
/// 外部提交数据对象到队列
/// </summary>
/// <param name="data"></param>
public
void
Post(
object
data)
{
PostData(
new
PoolData(data));
}
/// <summary>
/// 线程池主线程执行逻辑
/// </summary>
/// <param name="CompletionPortID"></param>
private
void
Run(
object
CompletionPortID)
{
SafeFileHandle completionPort = (SafeFileHandle)CompletionPortID;
while
(IsOpened)
{
uint
num;
IntPtr intPtr;
IntPtr value;
//从队列里取出最前面的对象
GetQueuedCompletionStatus(completionPort,
out
num,
out
intPtr,
out
value, 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>
private
void
RaiseExecute(
object
data)
{
Exceute?.Invoke(data);
}
/// <summary>
/// 触发ExitExecute事件
/// </summary>
/// <param name="data"></param>
private
void
RaiseExitExecute(
object
data)
{
ExitExceute?.Invoke(data);
}
/// <summary>
/// 结束线程池主线程
/// </summary>
public
void
Stop()
{
PostData(
new
PoolData(PoolCommand.Exit));
IsOpened =
false
;
}
/// <summary>
/// 内部提交数据到线程池队列中
/// </summary>
/// <param name="data"></param>
private
void
PostData(PoolData data)
{
if
(complatePort.IsClosed)
{
return
;
}
GCHandle value = GCHandle.Alloc(data);
PostQueuedCompletionStatus(complatePort, (
uint
)IntPtr.Size, IntPtr.Zero, GCHandle.ToIntPtr(value));
}
public
void
Dispose()
{
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!
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
"作者:" 数据酷软件工作室
"出处:" http://datacool.cnblogs.com
"专注于CMS(综合赋码系统),MES,WCS(智能仓储设备控制系统),WMS,商超,桑拿、餐饮、客房、足浴等行业收银系统的开发,15年+从业经验。因为专业,所以出色。"
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
"作者:" 数据酷软件工作室
"出处:" http://datacool.cnblogs.com
"专注于CMS(综合赋码系统),MES,WCS(智能仓储设备控制系统),WMS,商超,桑拿、餐饮、客房、足浴等行业收银系统的开发,15年+从业经验。因为专业,所以出色。"
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
2014-02-22 五有老码农,程序人生回顾:心安也不是归处啊