
C#におけるSleep処理とは?
目次
C#におけるSleep処理とは?
Thread.Sleepメソッド
C#の「Thread.Sleep」メソッド(「System.Threading」に存在)は、指定した時間の長さ、現在のスレッドを中断する処理です。このため、一般的に時間待機をするプログラムであれば良く用いられるメソッドです。またC#含め、「.NET Framework」の「Sleep」メソッドは「CPUの割り込み処理」で処理されます。このため、厳密な意味では引数で指定した時間待機処理をする訳ではない点には注意が必要です。単純な待機処理でかつ、厳密な時間計測が必要でない場合に利用します。なお「CPUの割り込み処理」が他のプログラムからも頻発する場合、CPUが別プロセスに利用されてしまいますので、「Thread.Sleep」メソッドを呼び出した際に設定した時間以上かかる場合があります。
Thread(スレッド)とは?
「Thread.Sleep」にある「Thread(スレッド)」という言葉を聞いて、ピンと来ない人も多いと思います。正直なところ、初心者の方はここで躓く事が多いのも事実です。C#以外のプログラムも含め、アプリケーションは2つに大別され、「マルチスレッド」「シングルスレッド」処理と呼ばれます。
「マルチスレッド」とは、ふんわり説明すると『聖徳太子』のように色々な話を同時にしても、ちゃんと聞き分けてそれぞれの担当者に適切に並列で指示をするような処理です。対義語として「シングルスレッド」というものがありますが、簡単に説明すると『1つの事しか片づけられない』人を思い浮かべてみてください。これをプログラムに当てはめて考えたのが「シングルスレッド」のプログラムになります。
正しく説明すると、例えばスマホアプリは基本的には「シングルスレッド」(同期処理)なので、アプリを切り替えないと裏に回ったアプリ処理は中断しています。※スマホの中でも、実際には電話やメール、その他通知機能を利用したバックグラウンドサービスはマルチスレッドで動いています。スマホと比較して、WindowsやMac、その他OSのプログラムは、同時に並列でプログラム処理を実行(非同期処理)しています。(Aプログラムを実行中に、待ち時間にブラウザで調べものをするような感じです。)
昔はタイムシェアリングという、1つのCPUで複数処理を同時にするために、CPU時間を等分配して細かな時間単位でCPU専用率を分割して、少ないリソース(資源)を共有利用していました。今やCPUにはコアと呼ばれる「脳」に相当するものが複数存在しますので、それぞれプログラム(スレッド)毎に自動的にCPUコアを割り当てて実行しているという形になります。(実際には、コアが複数あったとしても、それぞれの負荷に応じて処理が適切に分配されています。)「マルチスレッド」も「シングルスレッド」も、一長一短ですので、求められる処理によって使い分けが必要です。
Sleepメソッドの記述方法
C#では、2つの「Sleep」メソッドが準備されています。①引数にint型のミリ秒を利用した場合
1 |
public static void Sleep (int millisecondsTimeout); |
引数「millisecondsTimeout」には、待機するミリ秒を記載します(1秒なら””1000″”を記載)
②引数にTimeSpanクラスを利用した場合
1 |
public static void Sleep (TimeSpan timeout); |
引数の「timeout」には、TimeSpan クラス変数で定義した引数を指定します。(詳細な説明は後述します。)いずれも引数が負の数である場合や、「Int32.MaxValue」である””2147483647″”を超える場合は「ArgumentOutOfRangeException」例外が発生します。
スレッド内の非同期処理について
ここからはC#において、スレッド処理を非同期にする際に「Thread.Sleep」を利用する場合の注意事項について触れたいと思います。C#において「マルチスレッド」の【非同期処理】を定義する場合、同一クラス内に「async」シグネチャを付けたメソッドが必要になり、これらメソッドが『非同期メソッド』になります。C#の『非同期メソッド』では、「await」キーワードが利用できるようになり、メソッドを呼び出す際に「await」キーワードを付ける事で、該当処理のタスク完了を待ち、その戻り値を取り出す事が可能になります。
この際に「Thread.Sleep」メソッドに「await」キーワードを付けると、『voidを待機することができません』というエラーになります。これはC#において、「void」メソッドに「await」キーワードを付けて呼び出す事が不可能であるために発生し、今回の「Thread.Sleep」メソッドが戻り値の無い「void」メソッド(関数)であることが原因です。このような場合、「void」型の「Thread.Sleep」メソッドではなく、「System.Threading.Tasks」に存在する、遅延を表す「Task」クラスを返却する「Task.Delay」メソッドを利用します。
1 |
using System;using System.Threading.Tasks;namespace testApp001 { class Test001 { async Task sleepAndAsync (String str) { Console.WriteLine (""sleepAndAsync Start(sleep)""); // await Thread.Sleep (2000); この呼び方がエラーになる await Task.Delay (2000); Console.WriteLine (""sleepAndAsync End""); return ""Argument:"" + str; } async void asyncProc () { Console.WriteLine (""asyncProc Start(call sleepAndAsync)""); var res = await sleepAndAsync (""call from asyncProc""); Console.WriteLine (res); Console.WriteLine (""asyncProc End""); } static void Main (string[] args) { Console.WriteLine (""Main Method Start""); var app = new Test001 (); app.asyncProc (); Console.WriteLine (""Main Method End""); } }} |
といった記述が必要になります。C#開発において【非同期処理】内で「Thread.Sleep」メソッドの利用は出来ません。「Task.Delay」メソッドを「await」シグネチャを付けて呼び出す事で非同期実行になります。処理結果は
1 |
Main Method StartasyncProc Start(call sleepAndAsync)sleepAndAsync Start(sleep)Main Method End |
となり、「Task.Deley」メソッドが非同期で実行されるので、””sleepAndAsync Start(sleep)””以降のコンソール出力が””Main Methood End””まで出力されないようになります。
厳密な時間で待機処理をする場合
冒頭で説明の通りC#で厳密な時間で待機処理をする場合、「Thread.Sleep」メソッドでは精度に限界があります。このような場合は以下のような実装で回避する事が可能です。
1 |
using System;using System.Diagnostics;using System.Threading;namespace TestApp002 { class Test002 { static void Main (string[] args) { Stopwatch sw = new Stopwatch (); sw.Start (); while (true) { Thread.Sleep (1); if (sw.ElapsedMilliseconds > 1000) break; } Console.WriteLine (sw.ElapsedMilliseconds); sw.Stop (); } }} |
実際に「Thread.Sleep(1000);」と記載するより、処理の中断時間が厳密に計測できます。なお処理結果には
1 |
1001 |
1 |
1002 |
と表示され、「Thread.Sleep」メソッドが他の処理割り込みで時間がずれるのを防ぐ効果があります。
最後に
「Thread.Sleep」メソッドは簡単に実装できます。しかし少しの注意を払う事で、更にC#プログラム実装の幅が広がります。マイクロソフトには『C#関連のドキュメント』というサイトがあり、色々なサンプルやリファレンスが充実しています。また更に高みを目指す方は、C#において「マルチスレッド」の説明を重点的に行っている文献も多数ありますので、色々と活用してみてください。