我在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 ...
随机推荐
- [转帖]两种Nginx日志切分方案,狼厂主要在用第1种
两种Nginx日志切分方案,狼厂主要在用第1种 nginx的日志切分问题一直是运维nginx时需要重点关注的.本文将简单说明下nginx支持的两种日志切分方式. 一.定时任务切分 所谓的定时任务切分, ...
- [转帖]NUMA导致的Oracle性能问题
https://www.cnblogs.com/realcp1018/p/6903721.html 背景简介: Oracle版本:11.2.0.4 OS 版本:OEL5.8 在一次Oracle的Dat ...
- [转帖]1. awk基础,awk介绍,awk基本语法,直接使用action,打印列,初识列和行,\$0、\$NF、NF,基础示例,begin模式,end模式
文章目录 前言 awk介绍 awk基本语法 直接使用action 打印列 初识列和行 \$0.\$NF.NF 基础示例 初识模式(begin end) 总结 友情链接 前言 本小节是awk基础入门课程 ...
- [转帖]exportfs命令
https://www.cnblogs.com/xzlive/p/9766388.html exportfs命令:功能说明 :NFS共享管理语法格式exportfs [必要参数][选择参数][目录]功 ...
- [转帖]Traefik中诡异的502和504问题
https://zhuanlan.zhihu.com/p/156138704 我们都知道在 Kubernetes 集群中通常会使用 Ingress 方案来统一代理集群内部的流量,而常用的 Ingres ...
- 最简单的以CentOS为base images 安装 Nodejs等操作的方法
镜像内安装NodeJS的简单方法 公司内有产品需要安装nodejs以便进行相关操作,Linux和Windows时没有问题,但是如果是镜像的话可能会稍微复杂一点, 这里简单进行一下总结, 以便备忘. 1 ...
- SAP Gateway Trace
最近在SAP PE项目上遇到一个需求要求记录所有的ODATA(含S4/CLOUD API)的报文. Below T-codes recording logging and tracing: /IWFN ...
- 京东金融APP-新交互技术“虚拟数字人”赋能世界杯主题营销
作者:平台研发部,智能服务与产品部 距离加文·伍德提出web3.0已经过去8年时间,这8年加文·伍德创建的以太坊大放异彩,同时由web3.0引出的数字人.元宇宙也生根发芽,茁壮成长,带来了非凡的用户体 ...
- node+express+ multer 实现文件上传入门
文件上传 文件上传需要借助一个中间件 multer 因此我们需要安装 cnpm install multer --save 前端界面 在express创建的项目下的 public/upload目录下创 ...
- 你对iframe知道多少
iframe 嵌套第三方页面出现的问题 我们需要通过一个接口获取被嵌套的地址. 然后将改地址赋值给iframe的src中,代码如下 <template> <div> <i ...