我正在制作一個軟體來在螢屏上顯示一個計時器并在用戶定義的特定時間運行某些操作。我找到了一種通過使用具有 16 毫秒間隔(大約 60FPS)的 DispatcherTimer 在獨立視窗上顯示計時器的方法,但現在我必須找到一種方法來在定義的時間運行這些操作。
我制作了一個QueueableStopwatch可以完成作業的組件。它的作業方式如下:
QueueAction給它一個物件陣列- 在內部為要運行一次的 QueueActions 創建一個陣列(按間隔排序)
- 在內部為要重復的 QueueActions 創建另一個陣列
- 它有一個內部
Stopwatch計算時間 - 該組件具有方法
Start()和Stop() Start()啟動內部秒表并在單獨的執行緒上啟動“佇列處理”回圈Stop()Stopwatch.IsRunning停止秒表并且“佇列處理”回圈在更改為 false時自行停止
“佇列處理”回圈執行以下操作:
- 保留對下一個
QueueAction要運行的參考。如果內部Stopwatch.Elapsed> 到被參考QueueAction.Interval的它的運行,并且參考更新到運行一次 QueueActions 陣列中的下一個 - 我們在條件下運行待重復陣列中的每個 QueueAction
Stopwatch.Elapsed / QueueAction.Interval - QueueAction.TimesExecuted >= 1。如果運行,我們會增加QueueAction.TimesExecuted一。
該解決方案是否足以作為運行關鍵操作的應用程式的“核心”來實施?
可以使用此處Stopwatch.IsRunning記錄的意外行為嗎?
這是組件代碼:
public class QueueAction
{
/// <summary>
/// Interval to run the action
/// </summary>
public TimeSpan Interval { get; set; }
/// <summary>
/// The current action to run
/// </summary>
public Action Action { get; set; }
/// <summary>
/// Dispatcher the action will be ran into
/// </summary>
public Dispatcher Dispatcher { get; set; }
/// <summary>
/// True if the action will be repeated
/// </summary>
public bool Repeat { get; set; }
}
public class QueueableStopwatch
{
private Stopwatch _stopwatch = new Stopwatch();
public TimeSpan Elapsed => _stopwatch.Elapsed;
private RepeatableQueueAction[] _repeatQueue = { };
private QueueAction[] _singleQueue = { };
public QueueAction[] Queue
{
get => _singleQueue;
set
{
_repeatQueue = value.Where(action => action.Repeat).Select(action => new RepeatableQueueAction { QueueAction = action }).ToArray();
_singleQueue = value.Where(action => !action.Repeat).OrderBy(action => action.Interval.TotalMilliseconds).ToArray();
}
}
public void Start()
{
if (_stopwatch.IsRunning)
throw new InvalidOperationException("The chronometer is already running");
_stopwatch.Start();
if(_singleQueue.Length > 0)
{
new Task(() =>
{
int i = 0;
QueueAction selectedAction = selectedAction = _singleQueue[i];
do
{
if (i < _singleQueue.Length && selectedAction.Interval <= _stopwatch.Elapsed) // Single time run queue
{
selectedAction.Dispatcher.Invoke(() => selectedAction.Action());
i ;
if(i < _singleQueue.Length)
selectedAction = _singleQueue[i];
}
foreach(var repetitionAction in _repeatQueue) // Repeat run queue
{
if(_stopwatch.Elapsed / repetitionAction.QueueAction.Interval - repetitionAction.Repetitions >= 1)
{
repetitionAction.QueueAction.Dispatcher.Invoke(() => repetitionAction.QueueAction.Action());
repetitionAction.Repetitions ;
}
}
}
while (_stopwatch.IsRunning);
}).Start();
}
}
public void Stop()
{
_stopwatch.Reset();
}
private class RepeatableQueueAction
{
public QueueAction QueueAction { get; set; }
public int Repetitions { get; set; }
}
}
如果你想運行它,這個 xaml 可以作業:
主視窗.xaml
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
Background="Black">
<StackPanel Orientation="Vertical">
<Label Name="lblMessage" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="56"/>
<Button Click="Button_Click" Content="Stop" HorizontalAlignment="Center"/>
</StackPanel>
</Window>
主視窗.cs
public partial class MainWindow : Window
{
QueueableStopwatch stopwatch = new QueueableStopwatch();
public MainWindow()
{
InitializeComponent();
stopwatch.Queue = new QueueAction[]
{
new QueueAction
{
Dispatcher = lblMessage.Dispatcher,
Interval = TimeSpan.FromSeconds(7),
Action = () => lblMessage.Content = $"[{stopwatch.Elapsed}]I run every 7 seconds",
Repeat = true
},
new QueueAction
{
Dispatcher = lblMessage.Dispatcher,
Interval = TimeSpan.FromSeconds(10),
Action = () => lblMessage.Content = $"[{stopwatch.Elapsed}]Queued first but ran at 10 seconds"
},
new QueueAction
{
Dispatcher = lblMessage.Dispatcher,
Interval = TimeSpan.FromSeconds(3),
Action = () => lblMessage.Content = $"[{stopwatch.Elapsed}]3 seconds elapsed"
}
};
stopwatch.Start();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
stopwatch.Stop();
}
}
uj5u.com熱心網友回復:
秒表對于這項作業來說是錯誤的工具,你實際上是在一個執行緒上旋轉等待,消耗 CPU 時間并可能導致其他執行緒被餓死。
該框架已經以定時器的形式提供了這個功能。如果您想在 UI 執行緒上運行操作,則調度計時器將是合適的。因此,對于您要安排的每個操作,請創建一個相應的計時器。如果您想預先決定是否應該重復該操作,您可能需要一個包裝器。
var timer = new DispatcherTimer(){ Interval = TimeSpan.FromSeconds(10) };
timer.Tick = (o, e) => {
lblMessage.Content = $"[{stopwatch.Elapsed}]Queued first but ran at 10 seconds"
timer.Stop();
};
timer.Start();
計時器的解析度取決于作業系統,但通常為 1-16 毫秒。對于 UI 程式,這應該足夠了,無論如何,螢屏渲染都會有各種小的延遲。如果您需要更好的解析度,可以使用 mediatimer。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/481289.html
