我在winform项目里使用“Windows I/O完成端口”的经验分享
少年!看你骨骼惊奇,是万中无一的练武奇才,我这儿有本武林秘籍,见与你有缘就送你了!
如来神掌
Windows I/O完成端口是一个我至今都说不好的话题,请宽容的接受我这不是科班出身的自学成才的野生程序员身份。以前在上海一公司做产品追溯的时候,我的老大拿出一本《Windows核心编程》经常向我吹嘘什么“ Windows I/O完成端口”编程模型的时候我是云里雾里。后来看了公司常用的一个叫“线程池”的类的源码,豁然有点醒悟了,不就是类似Queue这样的东西么?按先进先出顺序处理业务数据,这明明就不是线程池啊,误导人了。但是这个类确实挺好用的,公司它都使用了很多年了。不想独享特此分享出来。
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!
我在winform项目里使用“Windows I/O完成端口”的经验分享的更多相关文章
- 循序渐进开发WinForm项目(3)--Winform界面层的项目设计
随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...
- Winform开发框架之通用Windows摄像头调用拍照--SNF快速开发平台3.3-Spring.Net.Framework
今天做了一个windows系统下调用摄像头.进行开启.关闭.拍照.设置等等功能演示. 进行源码贡献,欢迎大家下载使用 一.DEMO效果如下: 二.DEMO演示代码如下: using SNF.Utili ...
- NPOI导入导出EXCEL通用类,供参考,可直接使用在WinForm项目中
以下是NPOI导入导出EXCEL通用类,是在别人的代码上进行优化的,兼容xls与xlsx文件格式,供参考,可直接使用在WinForm项目中,由于XSSFWorkbook类型的Write方法限制,Wri ...
- 循序渐进开发WinForm项目(6)--开发使用混合式Winform模块
1.Winform数据访问模式定义 传统的Winform程序模块:用于传统的数据库通讯获取数据,这种方式获取数据,方便快捷,可以用于常规的业务系统的场景,用于单机版软件或者基于局域网内的业务系统软件. ...
- 电梯多媒体WinForm项目Q&A总结
最近,我给一家公司做了个电梯多媒体软件,该软件使用C#编写,现在我将其中遇到的问题及其解决方法总结一下,以便下次再遇到同样的问题可以快速解决:同时,也给博友分享一下,共同学习,共同提高. 1.Ques ...
- 循序渐进开发WinForm项目(5)--Excel数据的导入导出操作
随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...
- 循序渐进开发WinForm项目(4)--Winform界面模块的集成使用
随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...
- 循序渐进开发WinForm项目(2)--项目代码的分析
随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...
- iOS项目生成通用Windows应用
WinObjc - 使用iOS项目生成通用Windows应用 Github上一周年的WinObjc项目最近发布了预览版本,终于等到了这一天.WinObjc项目就是Build 2015大会上微软宣布 ...
- VS2015 项目中 添加windows服务
1. 在项目中添加winows服务 今天刚刚为自己的项目添加了windows服务,以服务的形式运行后台系统,为前端提供接口服务,下面说一下具体怎么为vs项目添加windows服务 2. 添加Windo ...
随机推荐
- [转帖]Linux 下rsync命令详细整理
https://blog.csdn.net/weixin_44052462/article/details/116134761 rsync是一个功能非常强大的工具,其命令也有很多功能特色选项,我们下面 ...
- [转帖]《Linux性能优化实战》笔记(五)—— 不可中断进程与僵尸进程
一. 进程状态 1. 状态含义 从 ps或者 top 命令的输出中,可以看到处于不同状态的进程 R:Running 或 Runnable,表示进程在 CPU 的就绪队列中,正在运行或者正在等待运行 D ...
- Python学习之十一_Windows获取硬件信息
Python学习之十一_Windows获取硬件信息 简介 网上找了一些方法简单整理了下,可以快速获取部分信息 包含机器名称等. 以及序列号相关 部分学习来源: https://blog.51cto.c ...
- golang实现的 https 协议的四层代理和七层代理
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 四层代理 在 tcp 这一层转发很简单. http 协议是 ...
- 【学到了】golang的[]byte可以append string类型的数据
上代码: func Test_use_string(t *testing.T){ arr := make([]byte,0, 100) arr = append(arr, "abcd&quo ...
- Natapp 邀请码 积分
邀请码: 29F145FC 充值95折
- [洛谷]P1967-最小生成树-好题推荐
[NOIP2013 提高组] 货车运输 题目背景 NOIP2013 提高组 D1T3 题目描述 A 国有 \(n\) 座城市,编号从 \(1\) 到 \(n\),城市之间有 \(m\) 条双向道路.每 ...
- easyui 使用不同的url以获取不同数据源信息
转载 https://www.bbsmax.com/A/kjdw1x06JN/ https://blog.csdn.net/lixinhui199/article/details/50724081 参 ...
- Python笔记四之协程
本文首发于公众号:Hunter后端 原文链接:Python笔记四之协程 协程是一种运行在单线程下的并发编程模型,它的特点是能够在一个线程内实现多个任务的并发操作,通过在执行任务时主动让出执行权,让其他 ...
- 小样本学习在文心ERNIE3.0多分类任务应用--提示学习
小样本学习在文心ERNIE3.0多分类任务应用(提示学习) 项目链接: https://aistudio.baidu.com/aistudio/projectdetail/4438610?contri ...