我正在为老虎机比赛写一个小型计数器作为一个小家庭项目。我想实现一个倒计时器,我已经完成了以下测试:
private Thread countdownThread;
private delegate void UpdateTimer(string update);
UpdateTimer ut;
public LapCounterForm()
{
InitializeComponent();
//...
ut += updateTimer;
countdownThread = new Thread(new ThreadStart(startCountdown));
}
private void startCountdown()
{
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
System.Diagnostics.Stopwatch stopwatch = new Stopwatch();
long time = 0;
stopwatch.Start();
while (stopwatch.ElapsedMilliseconds <= 5000)
{
time = 5000 - stopwatch.ElapsedMilliseconds;
TimeSpan ts = TimeSpan.FromMilliseconds(time);
ut(ts.Minutes.ToString().PadLeft(2, '0') + ":" + ts.Seconds.ToString().PadLeft(2, '0') + ":" + ts.Milliseconds.ToString().PadLeft(3, '0'));
}
}
private void updateTimer(string text)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<String>(ut), new object[] { text });
}
else
{
lblCountdownClock.Text = text;
}
}
当我开始我的线程时,它有效。我得到了我想要的5秒倒计时,但我可以看到我在这个过程中使用了大量的CPU(我的8线程i7 2600k的12%)。
我想我可以通过每10毫秒而不是每毫秒更新UI来减少这种负担,但除了在if(time % 10 == 0)
之前使用TimeSpan
之外我不知道怎么做并且更新了用户界面,但我怀疑由于while循环,这将是效率低下的。
我是否重新发明轮子?我希望我的计时器尽可能准确(至少对于老虎机的单圈时间记录,也许UI不需要经常更新)。
编辑:我尝试按照评论中的建议评论实际的字符串操作和UI更新。现在,当我启动我的线程时,我的整个UI都会挂起,直到线程退出,我仍然可以获得12%的CPU使用率。我怀疑虽然循环占用了大量的CPU时间。
更新:我选择了Kohanz发布的多媒体计时器(here)以及Daniel的回答。我根本不再使用另一个线程,我只是制作其中一个定时器对象,并有一个tick事件处理程序来计算点击开始按钮和tick事件之间的时间。我甚至可以将我的刻度的周期设置为1ms,这样我就可以看到很酷的倒计时,而且显然使用了0%的CPU :)我对此非常满意。
答案 0 :(得分:2)
不,不要走这条路。你完全以错误的方式思考这个问题。你基本上强迫你的线程冻结没有任何好处。
基本上任何游戏都是这样运作的:你有一个更新循环,每当触发你就会做必要的东西。所以,例如,如果你想知道多少时间,你会问某种“计时器”,自发生了什么事以来已经过了多少
这是处理这个问题的更好方法:
class MyStopwatch {
private DateTime _startTime;
private DateTime _stopTime;
public void start() {
_running = true;
_startTime = DateTime.Now;
}
public void stop() {
_stopTime = DateTime.Now;
_running = false;
}
public double getTimePassed() {
if(_running) {
return (DateTime.Now - _startTime).TotalMilliseconds;
} else {
return (_stopTime - _startTime).TotalMilliseconds;
}
}
}
答案 1 :(得分:1)
事后有点,但这显示了一种可以达到所需要的方式:
public class LapTimer : IDisposable
{
private readonly System.Diagnostics.Stopwatch _stopWatch = new System.Diagnostics.Stopwatch();
private readonly ConcurrentDictionary<string, List<TimeSpan>> _carLapTimes = new ConcurrentDictionary<string, List<TimeSpan>>();
private readonly Action<TimeSpan> _countdownReportingDelegate;
private readonly TimeSpan _countdownReportingInterval;
private System.Threading.Timer _countDownTimer;
private TimeSpan _countdownTo = TimeSpan.FromSeconds(5);
public LapTimer(TimeSpan countdownReportingInterval, Action<TimeSpan> countdownReporter)
{
_countdownReportingInterval = countdownReportingInterval;
_countdownReportingDelegate = countdownReporter;
}
public void StartRace(TimeSpan countdownTo)
{
_carLapTimes.Clear();
_stopWatch.Restart();
_countdownTo = countdownTo;
_countDownTimer = new System.Threading.Timer(this.CountdownTimerCallback, null, _countdownReportingInterval, _countdownReportingInterval);
}
public void RaceComplete()
{
_stopWatch.Stop();
_countDownTimer.Dispose();
_countDownTimer = null;
}
public void CarCompletedLap(string carId)
{
var elapsed = _stopWatch.Elapsed;
_carLapTimes.AddOrUpdate(carId, new List<TimeSpan>(new[] { elapsed }), (k, list) => { list.Add(elapsed); return list; });
}
public IEnumerable<TimeSpan> GetLapTimesForCar(string carId)
{
List<TimeSpan> lapTimes = null;
if (_carLapTimes.TryGetValue(carId, out lapTimes))
{
yield return lapTimes[0];
for (int i = 1; i < lapTimes.Count; i++)
yield return lapTimes[i] - lapTimes[i - 1];
}
yield break;
}
private void CountdownTimerCallback(object state)
{
if (_countdownReportingDelegate != null)
_countdownReportingDelegate(_countdownTo - _stopWatch.Elapsed);
}
public void Dispose()
{
if (_countDownTimer != null)
{
_countDownTimer.Dispose();
_countDownTimer = null;
}
}
}
class Program
{
public static void Main(params string[] args)
{
using (var lapTimer = new LapTimer(TimeSpan.FromMilliseconds(100), remaining => Console.WriteLine(remaining)))
{
lapTimer.StartRace(TimeSpan.FromSeconds(5));
System.Threading.Thread.Sleep(2000);
lapTimer.RaceComplete();
}
Console.ReadLine();
}
}