2013-08-21 1 views
0

나는 작은 집 프로젝트로 슬롯 카 레이스 용 작은 바퀴 카운터를 쓰고있다. 카운트 다운 타이머를 구현하려면 테스트로 다음과 같이하십시오.카운트 다운 타이머/클록을보다 효율적으로 구현하는 방법은 무엇입니까?

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 개 스레드의 12 %, 2600k).

는 내가 단지 UI 10 밀리 초마다 대신 밀리 초마다 업데이트하여이 부하를 많이 줄일 수 있습니다 그림,하지만 난 어떻게 TimeSpan을하기 전에 if(time % 10 == 0)을 사용하고 UI를 업데이트하는 것보다 다른, 등을 수행하는 아무 생각하지만 while 루프 덕택에 비효율적 일 것이라고 생각합니다.

나는 바퀴를 다시 발명합니까? 나는 가능한 한 정확하게 내 타이머를 원한다. (적어도 슬롯 머신 랩타임 기록은 UI가 자주 업데이트 될 필요가 없다.)

편집 : 의견에서 제안 된대로 실제 문자열 조작 및 UI 업데이트를 주석 처리 해 보았습니다. 이제 내 스레드를 시작할 때 스레드가 종료 될 때까지 전체 UI가 멈추고 여전히 12 %의 CPU 사용률을 갖습니다. 나는 while 루프가 많은 CPU 시간을 소비하고 있다고 생각한다.

업데이트 : 나는 Kohanz가 올린 멀티미디어 타이머 (here)와 Daniel의 대답을 함께했습니다. 나는 더 이상 다른 스레드를 전혀 사용하지 않고 타이머 객체 중 하나를 만들고 시작 버튼과 틱 이벤트를 클릭하는 사이의 시간을 계산하는 틱 이벤트 핸들러를 갖습니다. 나는 심지어 내 시원한 카운트 다운을 얻을 수 있도록 내 틱의주기를 1ms로 설정할 수 있으며, 분명히 0 % CPU를 사용하고있다. 나는 이것에 상당히 만족하고있다.

+0

UI 업데이트를 담당하는 또 다른 스레드를 생성하여 업데이트 후 N 밀리 초 동안 절전 모드로 들어갈 수 있습니다. – OnoSendai

+0

Timer 클래스를 본 적이 있으십니까? http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx .Net에 있습니까? – hatchet

+3

내가 물어도 괜찮 으면 : 누가 10 밀리 초의 변화를 읽을 수 있습니까? 100 밀리 초나 200 밀리 초마다 충분한 업데이트가 필요하지 않습니까? 그런 다음 일반 타이머를 사용할 수도 있습니다. – Alex

답변

2

Dont, 그냥이 길로 내려 가라. 당신은 완전히 잘못된 생각으로 이것을 생각하고 있습니다. 기본적으로 스레드는 아무런 효과가 없도록 고정시킵니다.

기본적으로 모든 게임은 다음과 같이 작동합니다. 업데이트 루프가 있으며, 필요한 모든 작업을 트리거합니다.

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; 
     } 
    } 
} 
+0

흥미 롭습니다. 나는 이런 식으로 생각하지 않았다. 나는 실제 랩타임을 위해 그것을 분명히 할 것이지만, 나는 여전히 카운트 다운 타이머를 원한다. 나는 이것을 UI 업데이트보다 훨씬 덜 자주 결합 할 것이라고 생각한다. :) – Logan

+2

하지만 ... 여기에 문제가 있습니다. "while"-loop은 사용자 스레드가 작성한 스레드에서 CPU의 100 %를 사용함을 보장합니다. 시간이 지날 때까지 "다음 코드 행을 계속 시도하십시오"라고 말합니다. 그 스레드에서 ~ 100 % 사용을 보장합니다. –

+0

또한 현재와 같이 스레드가 트리거 할 때마다 해당 스레드가 UI를 업데이트합니다. 그래서 두 개의 쓰레드를 100 % 사용하게합니다. 이제는 어떤 프레임 워크를 사용할지 모르지만, 콜백을 트리거 할 때마다 UI가 "다시 그리기"하면 그 스레드에서 100 %를 소비합니다. 그러나 운 좋게, 그것은 사실이 아니다. 당신은 "무언가 일어난다"의 방법으로 그것에 대해 생각해야만합니다. 그러면 타이머를 읽고 얼마나 많은 시간이 지 났는지보아야합니다. "Lap starts", "Lap ends"또는 "경과 시간을 업데이트하고 표시해야하는 UI"로 정렬합니다. 그때 당신은 시간을 읽지 않으면, 그렇지 않다. –

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(); 
    } 
} 
+0

스톱워치를 사용하는 것이 실제로 방법입니다. 그러나이 예제는 사용법을 보여주는 복잡한 방법입니다. –

+0

@Daniel MesSer : 네 말이 맞아, 어쩌면 나는 조금 벗어났다. 랩타임을 추적하고 한 번에 UI 업데이트를 얻는 한 가지 방법을 제시하고자했습니다. – Alex

관련 문제