我在winform项目里使用“Windows I/O完成端口”的经验分享 - 数据酷软件
source link: https://www.cnblogs.com/datacool/p/18027003/CoolThearPool
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
少年!看你骨骼惊奇,是万中无一的练武奇才,我这儿有本武林秘籍,见与你有缘就送你了!
如来神掌
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!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK