0

ビューを実行して更新するストップウォッチにすぎない小さなプログラムがあります。8 秒ごとに、ストップ ウォッチはそのビューでもカウンターをインクリメントします。したがって、0、8、16、24 秒などで...カウンターは 1、2、3、4 などです。

ビューの XAML にはいくつかのアイテムがあります。1 つはストップウォッチを保持するための TextBlock で、もう 1 つは「8 秒が経過した」回数を表示するための別の TextBlock です。

<StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Left" >
    <Label Content="Run Time:" FontSize="16" FontWeight="Bold" Margin="10,0,0,0"/>
    <TextBlock Name="ClockTextBlock" Text="00:00:00:00" FontSize="16" Foreground="Red" Margin="5" FontWeight="Bold"/>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
    <Label Content="Sample Count:" FontSize="16" FontWeight="Bold" Margin="10,0,0,0"/>
    <TextBlock Text="0" Name="SampleCountDigit" Foreground="Red" FontSize="16" FontWeight="Bold" Margin="5"/>
</StackPanel> 

この xaml ファイルのコード ビハインドには、ストップウォッチを作成するディスパッチ タイマーを設定するためのコードが含まれています。

public partial class StopWatchView: UserControl
{
    private DispatcherTimer dt = new DispatcherTimer();
    private Stopwatch stopWatch = new Stopwatch();

    private string _currentTime = string.Empty;
    private int _sampleCount = 0;

    public StopWatchView()
    {
        InitializeComponent();

        dt.Tick += new EventHandler(dt_Tick);
        dt.Interval = new TimeSpan(0, 0, 0, 0, 1);
    }

    private void dt_Tick(object sender, EventArgs e)
    {
        if (stopWatch.IsRunning)
        {
            TimeSpan ts = stopWatch.Elapsed;

            _currentTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds,
                                        ts.Milliseconds/10);

            ClockTextBlock.Text = _currentTime;

            if (ts.Seconds%8 == 0)
            {
                _sampleCount++;
                SampleCountDigit.Text = _sampleCount.ToString();
            }
        }
    }

    private void StartButton_Click(object sender, RoutedEventArgs e)
    {
        ClockTextBlock.Foreground = Brushes.Green;
        stopWatch.Start();
        dt.Start();
    }

問題

コードはストップウォッチを更新するために適切に機能するため、ストップウォッチのように見え、機能します。dt_tick()が呼び出さif(stopWatch.IsRunning)れて true と評価されるたびに、ビューは滞りなく更新されます。私が問題に直面しているのは、 が true のときに_sampleCountがインクリメントされるときif(ts.Seconds%8 == 0)です。それが競合状態であるかどうかはわかりませんが(私はまだスレッドを学習しているため)、TextBox="SampleCountDigit"更新は8秒間隔ではなく、急速かつ不規則に行われます。正直なところ、私がスレッドについて知っていることから、これらのメンバー変数 (_sampleCount と _currentTime) の両方が dt_Tick() イベント ハンドラー コード内で更新されたときに、なぜこれが発生するのか、または競合状態になるのかがわかりません。

なぜこれが起こるのでしょうか? SampleCountDigit (ビュー) を 8 秒ごとに更新するにはどうすればよいですか? この要素の更新について新しいスレッドを作成する必要がありますか?

EDIT問題の動作をよりよく理解するためにSampleCountDigit.Text = _sampleCount.ToString();、コードにブレークポイントを設定すると、8、16、24秒などで完全に停止します...その後、ビューのSampleDigitCounterが適切に更新されます。

4

2 に答える 2

1

同じメソッドで両方の変数を変更しているため、同じスレッドで両方の変数を変更しているため、これはスレッドの問題ではないという直感は正しいです。

あなたの問題は実際には非常に簡単です。dt_tick() メソッドが毎秒複数回呼び出されていると思います。1 秒間に 2 回または 3 回イベントを発生させ、その特定の 1 秒間の "ts.Seconds" がたまたま 8 の倍数である場合、カウンターを 3 回インクリメントします。

ここで発生するもう 1 つの小さな問題は、「ts.TotalSeconds」ではなく「ts.Seconds」を使用していることです。これは、以下のソリューションでその影響を確認できます。

これを修正する最善の方法は、ts.TotalSeconds を使用してストップウォッチが開始されてからの絶対秒数を取得し、カウンターを更新するたびにその値を追跡することです。次に、最後の更新時刻と現在を比較して、8 秒が経過したかどうかを確認できます。ts.Seconds ではなく ts.TotalSeconds を使用することが重要です。ts.Seconds は単に「秒針」であり、0..59 の範囲内でしか動かないため、時間が逆戻りするなどの奇妙なことが発生する可能性があるためです。これを使って。

// Outside your dt_tick()
double lastIncrementTime = 0.0d;

// Inside your dt_tick(), just after ClockTextBlock.Text = _currentTime;
if (ts.TotalSeconds - lastIncrementTime >= 8.0d)
{
    // increment counter etc, then...
    lastIncrementTime = ts.TotalSeconds;
}

この方法では、コードを実行する頻度に関係なく、秒針の現在の値が 8 の倍数であるかどうかを確認するのではなく、最後にカウンターをインクリメントしてから 8 秒が経過したかどうかを実際に確認しています。 .

それが役立つことを願っています。

于 2013-04-16T20:59:59.623 に答える