C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[10]: Sleepの時間がPCによって異なる


(過去ログ 172 を表示中)

[トピック内 15 記事 (1 - 15 表示)]  << 0 >>

■99058 / inTopicNo.1)  Sleepの時間がPCによって異なる
  
□投稿者/ 魚 (1回)-(2022/02/04(Fri) 20:58:33)

分類:[.NET 全般] 


VB.NET2015を使用しております


バックグランドスレッドにて
Thread.Sleep(1)
を使用しているのですが、
PCによって約1ミリ秒待機するものと
約12ミリ秒待機してしまうものがあります。



CPUのリソースが足りていないのかと考えたのですが、
コア数が4個で他に何もソフトを立ち上げていない状態でも
12ミリ秒かかってしまうものもあります。


Thread.CurrentThread.Priority = ThreadPriority.Highest

に設定もしてみたのですが変化ありませんでした。

いずれもWindows10で、.NETのバージョンやPCスペックもほとんど同じです。

どうしても数ミリ秒単位で待機する必要があるのですが
どのようにすれば、約12ミリ秒待機してしまうPCのスリープ時間を短くするにはどうしたら良いですか?




引用返信 編集キー/
■99059 / inTopicNo.2)  Re[1]: Sleepの時間がPCによって異なる
□投稿者/ Azulean (1227回)-(2022/02/04(Fri) 21:15:46)
2022/02/04(Fri) 21:16:21 編集(投稿者)

No99058 (魚 さん) に返信
> Thread.Sleep(1)
> を使用しているのですが、
> PCによって約1ミリ秒待機するものと
> 約12ミリ秒待機してしまうものがあります。

「Sleep は指定した時間以上の待ち」としか保証されていませんので、再開は他のスレッド・プロセス・リソース状況によります。


> どうしても数ミリ秒単位で待機する必要があるのですが
> どのようにすれば、約12ミリ秒待機してしまうPCのスリープ時間を短くするにはどうしたら良いですか?

Windows で「数ミリ秒きちんと待つ」といったことは通常できません。
要件、仕様や実装の見直しを考えた方が良いでしょう。
引用返信 編集キー/
■99060 / inTopicNo.3)  Re[1]: Sleepの時間がPCによって異なる
□投稿者/ くま (153回)-(2022/02/04(Fri) 21:28:23)
2022/02/04(Fri) 21:35:19 編集(投稿者)

> どうしても数ミリ秒単位で待機する必要があるのですが
> どのようにすれば、約12ミリ秒待機してしまうPCのスリープ時間を短くするにはどうしたら良いですか?

うーん。まず約12ミリ秒は何で求めたか?だよね
PCでの時間取得をさかのぼると

高分解能パフォーマンスカウンタ
QueryPerformanceCounter APIにたどり着きます

これはPCが起動してからの時間を取得するのだけれども
QueryPerformanceFrequency APIでカウンタの周波数でどのくらいの精度で取得できるかが決まります。

https://dobon.net/vb/dotnet/system/stopwatch.html

でこの周波数がCPUによって微妙に違ってきたりします
http://www.charatsoft.com/develop/otogema/page/03timer/timer2.htm
※WindowsOSの種類やCPU等でいろんな話が出てきます。

計算上の丸め処理等で、windows自体は数ミリ秒は誤差として扱っているんだよね...
初めに戻るとまず「約12ミリ秒は本当に約12ミリ秒なのか?」という問題になります。

あとループで待機する方法とかもあるけど、結局は処理時間はかかるんでどうしても誤差が発生します。
他ソフトの使用状況等影響を受ける可能性が色々あるので
0.001秒単位で確実な制御は難しいかな?





引用返信 編集キー/
■99061 / inTopicNo.4)  Re[1]: Sleepの時間がPCによって異なる
□投稿者/ 古谷 (25回)-(2022/02/04(Fri) 21:37:57)
timeBeginPeriodでスレッドタイマの割り込み間隔を小さくしたらいんじゃないかな
デフォルトだと15ms前後が限界

昔はMedia Playerを起動してると動画やゲームが滑らかに動くようになってたんですよー
きっとMedia PlayerがtimeBeginPeriodをほにゃほにゃしてたんだろうなーしみじみ

引用返信 編集キー/
■99062 / inTopicNo.5)  Re[2]: Sleepの時間がPCによって異なる
□投稿者/ くま (154回)-(2022/02/04(Fri) 21:49:46)
追記

もし0.001秒単位で処理を実行するタイミングを調整できたとします。

今度は「その処理が毎回同じタイミングで処理を完了することができるか?」という問題が発生します。

バックグランドスレッドの時点でフォアグラウンドに処理を譲っている状態ですから同じ時間で完了させる事は難しいですね。
引用返信 編集キー/
■99063 / inTopicNo.6)  Re[3]: Sleepの時間がPCによって異なる
□投稿者/ 魚 (2回)-(2022/02/04(Fri) 22:03:59)
ありがとうございます。

確認方法ですが



Dim www As New Stopwatch
www.Restart()


Dim hh = www.ElapsedMilliseconds

を100回とかループを回して計測しています。

そもそも

For i = 1 to 10000

Sleep(1)

Next

としただけで体感速度が10倍違うので測定しなくとも明らかに違いが分かります。

> 0.001秒単位で確実な制御は難しいかな?

0.001秒単位で確実な制御は必要としていません。

せいぜい2〜5ミリ秒程度のオーダーでも問題無く
それも毎回同じ時間である必要もありません。

とにかく10ミリ秒以上というのは遅すぎるので使い物にならないので
せめてこれの倍速は出したいと考えています。


> timeBeginPeriodでスレッドタイマの割り込み間隔を小さくしたらいんじゃないかな
> デフォルトだと15ms前後が限界

PCによっては1〜2ミリ秒程度の待機時間もできていますが
これは何が違うのでしょうか?


いま、スリープ時間の長いPCが手元にないため、
まだ試せていませんが、
http://www.charatsoft.com/develop/otogema/page/02window/sleep.html

このページによると
timeBeginPeriod()とtimeEndPeriod()
を実行すると実現できるのでしょうか?


引用返信 編集キー/
■99064 / inTopicNo.7)  Re[4]: Sleepの時間がPCによって異なる
□投稿者/ 魚 (3回)-(2022/02/04(Fri) 22:26:01)
いま、手元にあるスリープ時間の短いPCでとりあえず試しているのですが


Call timeBeginPeriod(1000)

Dim www As New Stopwatch
www.Restart()

Thread.Sleep(1)


Dim hh = www.ElapsedMilliseconds

とやっても時間は毎回1ミリ秒になります。
なぜうまく設定できていないのでしょうか?



引用返信 編集キー/
■99065 / inTopicNo.8)  Re[5]: Sleepの時間がPCによって異なる
□投稿者/ radian (8回)-(2022/02/04(Fri) 23:52:56)
2022/02/05(Sat) 00:37:52 編集(投稿者)

No99064 (魚 さん) に返信
> いま、手元にあるスリープ時間の短いPCでとりあえず試しているのですが
>
>
> Call timeBeginPeriod(1000)
>
> Dim www As New Stopwatch
> www.Restart()
>
> Thread.Sleep(1)
>
>
> Dim hh = www.ElapsedMilliseconds
>
> とやっても時間は毎回1ミリ秒になります。
> なぜうまく設定できていないのでしょうか?

APIの仕様はブラックボックスなので、そういうもんだとしか言いようがないですね。
そもそも、何故そんな大きい値を設定しているのでしょう?
タイマーの精度を上げたいのであれば、timeBeginPeriod(1) でいいですよね?
timeBeginPeriodを使うのって、普通はデフォルトより精度上げたい場合なので、
わざわざ大きい値を設定して検証してる人自体殆ど居ないんじゃないですかね。

いずれにせよ、精度を上げたところで、Sleepで指定した値で
きっちり待機する保障は無いので、timeBeginPeriod(1) を指定しても、
システムの負荷によって10msec以上待機したりする事もあります。
.NETコード上のスレッドだと、GCの影響を受ける場合もあるので、更に安定しません。
定周期で実行したい処理などは、timeSetEventを使用するのが
タイミングが安定してたとは思いますが。

[マルチスレッド環境で特定の処理を処理時間のばらつきなく高速化する方法]
https://social.msdn.microsoft.com/Forums/ja-JP/c3ad7905-5c88-4e4d-98fd-e08a6edb34e6/12510125231248112473125241248312489298722265912391293052345012?forum=csharpgeneralja

殆どこれと同じような話なんじゃないですかね?
Windowsのタイマー精度問題は昔から言われているので、
関連APIとかググるだけでも色々情報出てくるとは思いますが。
引用返信 編集キー/
■99066 / inTopicNo.9)  Re[4]: Sleepの時間がPCによって異なる
□投稿者/ 古谷 (26回)-(2022/02/05(Sat) 00:09:20)
No99063 (魚 さん) に返信
> PCによっては1〜2ミリ秒程度の待機時間もできていますが
> これは何が違うのでしょうか?

別のアプリがtimeBeginPeriodを呼んでるとそういうこと起こります
私のMedia Playerしみじみストーリーと全く同じです

ググったところChromeを起動してるとタイマの短くなるとかならないとか
そういう情報もありました、Chromeに限らずそういうアプリはあるでしょう

> Call timeBeginPeriod(1000)
> とやっても時間は毎回1ミリ秒になります。

timeBeginPeriodは最も短い値がOSにセットされます
別のアプリが短い時間をセットしてるんだと思いますよ

引用返信 編集キー/
■99067 / inTopicNo.10)  Re[5]: Sleepの時間がPCによって異なる
□投稿者/ くま (155回)-(2022/02/05(Sat) 10:30:32)
元々の発想として他の方が書かれている通り

Thread.Sleep(1)

は0.001秒止めるのではなく
スレッドをとめて「他の処理に実行権限を与え」0.001秒以上たったら自分自身を動かす
という意味です。

という事はOS自体かまたは別のソフトが動いていれば、そちらの処理が動き
結果として0.001秒以上後に処理が再開されます。

Thread.Priority プロパティ
https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.thread.priority?view=net-6.0
https://codezine.jp/article/detail/141#priority

精度を上げるならスレッド自身の優先順位を上げて他の処理が行われないようにするしかありません。
その場合画面ロックなどの影響が発生するでしょう。
また優先順位としての限界もあります。

timeSetEvent関数
http://www.yts.rdy.jp/vcpp/vc/vcpp5_1.html
https://www.ipentec.com/document/csharp-create-timer-using-timesetevent

timeBeginPeriodですが他のアプリに影響を与えます。
https://ryo021021.hatenablog.com/entry/2013/10/22/154551



引用返信 編集キー/
■99068 / inTopicNo.11)  Re[6]: Sleepの時間がPCによって異なる
□投稿者/ くま (156回)-(2022/02/05(Sat) 11:26:34)
Imports System.Windows.Forms.Application
Imports System.Runtime.InteropServices

Public Class Form1

    '高分解能パフォーマンスカウンタが存在する場合、そのカウンタの現在の値を取得します。
    Private Declare Function QueryPerformanceCounter Lib "kernel32" (
        ByRef lpPerformanceCount As Long) As Int32

    '高分解能パフォーマンスカウンタが存在する場合、そのカウンタの周波数(更新頻度)を取得します。
    Private Declare Function QueryPerformanceFrequency Lib "kernel32" (
        ByRef lpFrequency As Long) As Int32

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button2.Click
        '高分解能パフォーマンスカウンタ精度取得
        Dim lngFreq As Long             'カウンタ精度
        QueryPerformanceFrequency(lngFreq)

        '開始時刻取得
        Dim lngStartTime As Long        '開始時間
        Dim lngEndTime As Long          '※計測用終了時間
        Dim lngLoopTime As Long         'ループ中時間
        Dim lngLoopEndTime As Long      'ループ終了時間
        Dim lngLoopCount As Long        '※計測用ループ回数
        Call QueryPerformanceCounter(lngStartTime)

        '終了時刻設定(0.001秒)
        lngLoopEndTime = lngStartTime + (lngFreq / 1000)

        '終了時刻を過ぎるまでループ
        Do
            DoEvents()
            lngLoopCount += 1           '※ループ回数
            Call QueryPerformanceCounter(lngLoopTime)
        Loop Until lngLoopEndTime <= lngLoopTime

        '※終了時刻取得
        Call QueryPerformanceCounter(lngEndTime)

        '※実行結果
        Debug.Print("{0}ミリ秒 {1}回", (lngEndTime - lngStartTime) / lngFreq, lngLoopCount)
    End Sub
End Class

(※は測定用に入れた処理の為、本来は不要)

フォームにボタン1つ張り付けて試すと分かると思うけど、0.001秒単位にそろえても実行毎にループ回数に大きな差が発生するでしょ?
これは「DoEvents()」でOS上処理をしなければならない制御が発生しているっていう意味。
厳密には違うんだけど「Thread.Sleep(1)」が0.001秒でない原因の一つではあります。

「DoEvents()」をコメントアウトすれば回数は安定するけど、今度は画面ロック等発生します。
バックグランドスレッドならこの誤差がさらに増大します。

ちなみにこの方法は他の処理の優先順位に影響を与えますのでバックグランドスレッドには合わない処理です。

まあこの辺り昔から先人が苦労してきたけど結局「0.01秒程度の誤差は発生する物」というのが
Windows上ではお約束となっているって事です。

引用返信 編集キー/
■99111 / inTopicNo.12)  Re[7]: Sleepの時間がPCによって異なる
□投稿者/ radian (9回)-(2022/02/06(Sun) 16:04:07)
StopwatchクラスはQueryPerformance系APIベースなので、
直接API使わなくても大丈夫ですよ。
引用返信 編集キー/
■99112 / inTopicNo.13)  Re[8]: Sleepの時間がPCによって異なる
□投稿者/ くま (159回)-(2022/02/06(Sun) 17:08:27)
No99111 (radian さん) に返信
> StopwatchクラスはQueryPerformance系APIベースなので、
> 直接API使わなくても大丈夫ですよ。

確かにそうです。
ただ今回例としてできるだけ間に処理を入れてくなかったので。

実装する場合49.7日間制限とかあるのでStopwatchクラスを利用するほうがよいですね。

引用返信 編集キー/
■99133 / inTopicNo.14)  Re[9]: Sleepの時間がPCによって異なる
□投稿者/ 魚 (4回)-(2022/02/07(Mon) 21:55:10)
おかげさまで
Call timeBeginPeriod(1)
を使うことで解決することができました。

ただ。古谷さんの仰るようにGoogleChromeを起動して計測してみましたが
変化ありませんでした。バージョンによるのでしょうか・・?

あと、Call timeBeginPeriod(1000)
のように数値を大きくして実行しても変化ありませんでした。
減らす方向にしか使えない仕様なのでしょうか・・?
解決済み
引用返信 編集キー/
■99135 / inTopicNo.15)  Re[10]: Sleepの時間がPCによって異なる
□投稿者/ Azulean (1228回)-(2022/02/08(Tue) 07:05:06)
No99133 (魚 さん) に返信
> ただ。古谷さんの仰るようにGoogleChromeを起動して計測してみましたが
> 変化ありませんでした。バージョンによるのでしょうか・・?

このあたりはちゃんと説明を読みなさいやという話かと。

https://docs.microsoft.com/ja-jp/windows/win32/api/timeapi/nf-timeapi-timebeginperiod
> Starting with Windows 10, version 2004, this function no longer affects global timer resolution.

あとこの辺も。
> Starting with Windows 11, if a window-owning process becomes fully occluded, minimized, or otherwise invisible or inaudible to the end user, Windows does not guarantee a higher resolution than the default system resolution. See SetProcessInformation for more information on this behavior.


バックグラウンドプロセスなら、Windows 11 からは期待通りに動かないということでしょう。
元々マルチメディア系の API として提供され、それが他のことにも作用していたに過ぎないので、Thread.Sleep(1) を実現するための手段ではないと言うことです。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -