時間, スレッド,キューに関するモジュール
Back to index page
コンピュータでデータ計測を行う場合,データの取得時間を記録することは基本中の基本です.コンピュータは必ず内部時計を持っているので,その時計を呼び出して時間を取得します.通常コンピュータは1970年1月1日午前0時からの時間をカウントしているので,それを利用します.秒単位で持っている値なので非常に大きな値であり,そのままでは使用できません.
まずは,time モジュールを使って時間を見てみましょう.
from time import * print(time()) |
実行すると以下のような結果となります.
1766448941.7837431 |
1970年1月1日から55年と358日ですが,うるう年などもあるので,56年とちょっと経っている計算になります.小数部以下は100nsまでありますが,コンピュータの時間は10ms程度までしか当てにできません.
さて,time モジュールですが,やはり多くの機能が含まれていますので,全部は紹介しきれません.また,ここで注意が必要なのは,上のようなサンプルプログラムを保存する際に time.py という名前を使ってはいけないことです.以前にも説明しましたが,本来のモジュール名が time.py なので自作のプログラムをその名前にしてしまうと呼び出し矛盾が生じてエラーになります.
では,time() 関数を使用して経過時間を知るにはどうすればよいかというと,計測開始時に一度時間を取得し,データが得られた時間に再度時間を取得して引き算することで求められます.
4桁の素数を全部探すプログラムを作って,その計算時間を測定してみましょう.
from time import *
start = time()
for i in range(1000, 9999):
for j in range(2, i):
if i % j == 0:
break
else:
print(i, end = ' ')
else:
print()
now = time() - start
print(f'Elapsed time: {now}')
|
結果は以下のようになりました.
1009 1013 1019 1021 1031 1033 1039 1049 1051 1061 1063 1069 1087 1091 1093
1097 1103 1109 1117 1123 1129 1151 1153 1163 1171 1181 1187 1193 1201 1213
1217 1223 1229 1231 1237 1249 1259 1277 1279 1283 1289 1291 1297 1301 1303
1307 1319 1321 1327 1361 1367 1373 1381 1399 1409 1423 1427 1429 1433 1439
中略
9661 9677 9679 9689 9697 9719 9721 9733 9739 9743 9749 9767 9769 9781 9787
9791 9803 9811 9817 9829 9833 9839 9851 9857 9859 9871 9883 9887 9901 9907
9923 9929 9931 9941 9949 9967 9973
Elapsed time: 153.581280708313
|
ということで,私の環境では153秒かかりました.
IDLEはインタラクティブに作業するところが重要なのですが,そのせいで動作は遅くなります.Windows に標準で備わっているコマンドプロンプトを使用すると,なんと 0.34 秒で終わります!
秒数だけでいろんなことを表すのは不便ですので,我々が普段馴染んでいる「時刻」で表示できる datetime モジュールも用意されています.ただし,扱いにはこちらも注意が必要です.まずは,現在時刻を取得してみましょう.
from datetime import * print(time()) |
結果ですが,以下のようになり,ちゃんと時刻を表示してくれませんでした.
00:00:00 |
以下のように一部を変更します.
from datetime import * print(datetime.now()) |
するとちゃんと時刻を表示してくれました.
2025-12-23 09:48:43.991163 |
いくつかややこしい点があります.datetime モジュールの中の datetime メソッドを呼び出すだけではダメで,それに now モジュールも作用させなければなりません.
実験データを計測中に自動で保存する設定にした場合,保存するファイル名を毎回変更しないと,貴重なデータを上書きで消してしまうことになってしまいます.そのたびにファイル保存ダイアローグを表示して自分でファイル名を入力するのは面倒なので,現在時刻を取得してそれをファイル名に入れることで上書き防止を行うことがよく行われます.俗にタイムスタンプといいます.
ただ,秒数が上の例のように長々と続くのは分かりづらいので,年月日と時刻を整形して付け加えるのが便利です.その際に datetime が返す時刻オブジェクトを文字列に変換してくれる strftime メソッドを使用します.これまで紹介していなかった %記法を使用しますが,以下の例に示すように単純なので,すぐに理解できると思います.
from datetime import *
date = datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
print(date)
|
| 2025-12-23-09-59-23 |
記号の意味は分かると思いますが,Y が年,m が月,d は日,H が時間で M が分,S が秒です.時刻関係が大文字で規定されているので,日付に関する方は文字が重複すると小文字になります.
測定に1秒以上かかる場合は上の書式で十分ですが,1秒以下で終了する測定を自動で続ける場合にはマイクロ秒も追加しなければいけません.その際には %f をつけます.
一定の時間間隔でデータを取得したい場合には sleep 関数を使用します.引数に秒単位の値を指定します.
from time import *
from random import *
for _ in range(10):
print(randint(10, 99))
sleep(0.5)
|
上のプログラムは2桁の正の整数を乱数により10回発生させますが,0.5 秒間隔で表示します.
0.00, 43 0.51, 98 1.02, 94 1.53, 42 2.04, 40 2.55, 65 3.06, 93 3.57, 89 4.08, 44 4.59, 85 |
スレッドという言葉自体は最近のソーシャルメディアなどで関連する話題の一連の流れを表す言葉としてすでに日常的に使用されています.プログラミングにおいても同じような意味なのですが,それは複数の処理を並列して行う際に,それぞれの関連ある処理を指してスレッドと言います.2つ以上の関数を同時に動作させるときに使用します.
スレッドもモジュールを読み込んで実行しますが,モジュール名は threading とちょっと変えてあるので,それも注意してください.
今回使用するのはカンマ区切りのデータで引用符でくくらない形式とします.
次に示すプログラムは1秒に1回3桁の乱数を発生させる処理と,3秒に1回4桁の乱数を発生させることを並列に行うものです.
from time import *
from random import *
from threading import *
def digits_3():
for _ in range(10):
sleep(1)
print(randint(100, 999))
def digits_4():
for _ in range(5):
sleep(3)
print(randint(1000, 9999))
thread1 = Thread(target = digits_3)
thread2 = Thread(target = digits_4)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
|
実行すると画面には以下のように乱数が表示されます.
429 406 3721 743 392 961 2024 628 893 763 5655 267 612 5152 1221 |
動作的には次の図のようになっています.

カタカナでキューと書くとテレビ番組の撮影などで開始の合図を送るときの表現のように思われますが,元の綴りは queue でこれは待ち行列のことです.混んでいるお店で注文するために並んでいる人の列を queue と言います.
プログラミングでは処理を待つ間列に並んでいるようなイメージです.特に何も指定しなければ「先入れ先出し」で,キューに入った順に処理されます.スレッドを使用すると複数の処理が同時に動いていますので,その動作によってなされた処理が順番に並んでいるような状態で,データを安全にやりとりするためのメソッドです.queue クラスをインポートして使用します.
from random import * from queue import * data_queue = Queue() data_queue.put(randint(10, 99)) value = data_queue.get() print(value) |
上のようにとりあえずキューを作成し,put でキューに値を追加し,get で値を取り出すのが基本です.

以下の動作を行うプログラムを考えます.
from random import *
from queue import *
from time import *
from threading import *
from csv import *
def value3():
for _ in range(20):
sleep(1 / randint(2, 5))
t = time() - start
value = randint(10, 99)
data_queue.put((t, value))
print(f'{t:.2f}, {value}')
def value4():
for _ in range(20):
sleep(1 / randint(2, 5))
t = time() - start
value = randint(100, 999)
data_queue.put((t, value))
print(f'{t:.2f}, {value}')
def data_write():
tt = 1
while tt <= 20:
d1, d2 = data_queue.get()
with open('random_num.csv', 'a', newline = '') as f:
write_data = writer(f)
write_data.writerow([d1, d2])
tt += 1
start = time()
data_queue = Queue()
thread1 = Thread(target = value3)
thread2 = Thread(target = value4)
thread3 = Thread(target = data_write)
thread1.start()
thread2.start()
thread3.start()
thread1.join()
thread2.join()
thread3.join()
|
実行すると以下のように乱数が表示され,同時にファイルがフォルダに保存されています.
0.25, 50 0.33, 375 0.59, 5620.59, 44 1.14, 6351.14, 90 1.35, 675 1.40, 21 1.60, 756 1.74, 24 1.85, 274 2.03, 23 2.10, 596 2.37, 90 2.40, 728 2.60, 73 2.85, 65 2.91, 487 3.10, 69 3.16, 765 3.50, 461 3.66, 63 3.74, 131 4.00, 33 4.14, 282 4.39, 65 4.63, 29 4.68, 405 4.88, 674.88, 303 5.20, 16 5.45, 377 5.54, 26 5.79, 401 5.92, 85 6.16, 236.17, 807 6.41, 113 6.46, 96 6.79, 687 |
画面上ではデータの表示が崩れているところがありますが,保存されているデータは問題ありません.

上のプログラムでは保存するCSVファイルのファイル名を random_num.csv と決め打ちしていましたが,実行するたびに上書きしないようにタイムスタンプを追加してみましょう.ただし,ここで注意が必要です.time モジュールと datetime モジュールには両方とも time() というメソッドがあるため,これまで import 文で使用してきたアスタリスク * が使えません.面倒でも以下のようにモジュール名.メソッドのようにしてください.
from random import * from queue import * import time from threading import * from csv import * import datetime |
使用する関数 time.time() time.sleep() datetime.datetime() |
ファイル名を作成する際には文字列は加算できることを使用します.