后台周期计时
用于计时后台任务的高精度计时器 EventArg 类。
引言
我经常需要监控后台进程中发生的事情,并且厌倦了以各种方式重写这段代码。所以我想分享我所拥有的,特别是考虑到我从大家那里得到了很多有用的代码片段。这是我提交的第一篇文章,在源代码格式化方面遇到了一些小麻烦;)。源代码本身格式正确。
背景
基本上,高精度计时函数来自微软:http://msdn.microsoft.com/en-us/library/aa964692(v=vs.85).aspx
那个例子是针对 Windows CE 的,所以我将 DllIMports 更改为“kernel32.dll”,而不是像他们示例中的“coredll.dll”,并添加了各种循环计时/显示方法。
然后我将其继承到一个非常基本的 EventArg 类中,就像 BackgroundWorker 类中使用的那样。因此,我的后台进程的计时细节将始终易于获取,因为这些参数会从 EventHandler 传递到 EventHandler,略有变化。
使用代码
除了 HiResTimer 类之外,我尽量使其保持简单。继承它的 EventArg 类非常基本,但您可以根据特定目的添加您想要的内容。HiResTimer 类的想法是一次编写,并轻松获得高分辨率计时细节,并且对实际执行的工作来说是某种程度的透明的。
除了将 HiResTimer 作为 EventArgs 类的一个基础之外,以下说明了以独立方式使用该类的方法
// add the ClassHighResTimer.cs file to your project
// add a StatusStrip with a label and a progress bar
// and a Button to your Form
// add these to your using statements
// using AbsoluteGenius;
// using System.Threading;
//
// then put this in as your button1_Click event code
private void button1_Click(object sender, EventArgs e)
{
// this creates and inits cycle timing
Utility.HiResTimer hiResTimer = new Utility.HiResTimer();
Int64 totalCount = 1000; // loop counter total count
// if there's a lot of processing before start
// hiResTimer.StartTime = DateTime.Now; // (optional) will re-init cycle timing
while (hiResTimer.CycleCount != totalCount - 1)
{
//
// do whatever work intended here
Thread.Sleep(100);
//
hiResTimer.UpdateCycleStats(); // updates cycle stats and hiResTimer.CycleCount
toolStripStatusLabel1.Text = " Run Time: " + hiResTimer.RunningTime
+ " Avg Time: " + hiResTimer.CycleAvgTime
+ " Cycle Time: " + hiResTimer.CycleTime;
toolStripProgressBar1.Value = hiResTimer.CyclePercentage(totalCount);
statusStrip1.Refresh();
}
// do whatever post-loop processing (display/save data, etc.)
// hiResTimer.EndTime = DateTime.Now; // (optional) logs completion of overall task
toolStripStatusLabel1.Text = " Run Time: " + hiResTimer.RunningTime // the total while loop time
+ " Avg Time: " + hiResTimer.CycleAvgTime// average while loop interval
+ " Total Time: " + hiResTimer.TotalTime;// total task time
// (also logs completion and EndTime)
// and that's all there is to it
}
EventArgs 类只是继承了相同的功能
public class HiResEventArgs : HiResTimer
{
#region "Members"
private object argument = null;
public object RealResult = null;
public object RealUserState = null;
#endregion
#region "Construction"
public HiResEventArgs(object argument)
: base() // base() inits the cycle timing
{
this.argument = argument; // pick up the argument
}
public static HiResEventArgs CopyOfHiResEventArgs(HiResEventArgs hiResEventArgs)
{
HiResEventArgs returnCopy = new HiResEventArgs(hiResEventArgs.argument);
returnCopy.RealUserState = hiResEventArgs.RealUserState;
returnCopy.RealResult = hiResEventArgs.RealResult;
returnCopy.CopyHiResTimer(hiResEventArgs);
return returnCopy;
}
#endregion
#region "Properties"
public object Argument
{
get { return argument; }
}
#endregion
}
因此,例如,将上述独立代码移动到 BackgroundWorker 类的 DoWork 函数中,将会非常相似,除了它将使用 HiResEventArgs 变量作为发送到它的参数/参数。
Utility.HiResEventArgs hiResArgs = new Utility.HiResEventArgs(100); // 100 loop total as argument
backgroundWorker1.RunWorkerAsync(hiResArgs);
DoWork 函数将接收到它并像独立示例一样使用它,除了它不会显示任何内容
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
Utility.HiResEventArgs hiResArgs = e.Argument as Utility.HiResEventArgs;
// pick up the argument
Int64 totalCount = 0; // loop counter total count
Int64.TryParse((hiResArgs.Argument != null ? hiResArgs.Argument.ToString() : "100"), out totalCount);
// if there's a lot of processing before start
// hiResArgs.StartTime = DateTime.Now; // (optional) will re-init cycle timing
while (hiResArgs.CycleCount < totalCount && !bw.CancellationPending)
{
//
// do whatever work intended here
Thread.Sleep(100);
//
hiResArgs.UpdateCycleStats(); // updates cycle stats and cycle counter
hiResArgs.RealUserState = null; // (optional) fill this up with whatever state data you want
hiResArgs.RealResult = null; // (optional) fill this up with whatever result data you want
// send the hiResArgs to the ProgressChanged code as e.UserState
bw.ReportProgress(hiResArgs.CyclePercentage(totalCount), // (important) sets total cycles
Utility.HiResEventArgs.CopyOfHiResEventArgs(hiResArgs)); // send a copy as e.UserState
}
// send the hiResArgs to the RunWorkerCompleted code as e.Result
e.Result = Utility.HiResEventArgs.CopyOfHiResEventArgs(hiResArgs); // send a copy as e.Result
// finally make sure to flag Cancel if necessary
if (bw.CancellationPending)
e.Cancel = true;
}
ProgressChanged 函数接管了先前
在独立示例中直接处理的进度显示
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (((BackgroundWorker)sender).CancellationPending)
return;
if (e.UserState != null && e.UserState.GetType().ToString().EndsWith("HiResEventArgs"))
{
Utility.HiResEventArgs hiResArgs = e.UserState as Utility.HiResEventArgs;
toolStripStatusLabel1.Text = hiResArgs.CyclePercentage().ToString() + "% Done... "
+ " Run Time: " + hiResArgs.RunningTime
+ " Avg Time: " + hiResArgs.CycleAvgTime
+ " Cycle Time: " + hiResArgs.CycleTime;
toolStripProgressBar1.Value = hiResArgs.CyclePercentage();
}
else
toolStripProgressBar1.Value = e.ProgressPercentage;
statusStrip1.Refresh();
}
最终结果和 TotalTime 将在 RunWorkerCompleted 事件处理程序中传递
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == false && e.Error == null
&& e.Result != null && e.Result.GetType().ToString().EndsWith("HiResEventArgs"))
{
Utility.HiResEventArgs hiResArgs = e.Result as Utility.HiResEventArgs;
// do whatever post-loop processing (display/save data, etc.)
// hiResArgs.EndTime = DateTime.Now; // (optional) logs completion of overall task
toolStripStatusLabel1.Text = hiResArgs.CyclePercentage().ToString() + "% Completed "
+ " Run Time: " + hiResArgs.RunningTime // the total while loop time
+ " Avg Time: " + hiResArgs.CycleAvgTime // average while loop interval
+ " Total Time: " + hiResArgs.TotalTime; // total task time
// (also logs completion and EndTime)
}
button1.Text = "Start";
}
肖恩·奥利里 (Sean O'Leary)