○Original Class
・ArrayWarpperにあるmArray As Variantですが、Vatiantに関するドキュメントを参照して頂ければ
解ると思いますが、任意のオブジェクトを代入する事ができますので、基本的にList, Queue, Stackは
そのまま使って問題が無いと考えています。
DateTimeFirstDay As DateTime
1970/1/1のDateTimeを返します。
isHour24Range( inData As Integer ) As Boolean
Integer型の引数が0〜23の間であればTrueを戻し、 それ以外はFalseを戻します。
isMinAndSecRange( inData As Integer ) As Boolean
Integer型の引数が0〜59の間であればTrueを戻し、 それ以外はFalseを戻します。
isRange( inEvalValue As Integer, inLowValue As Integer, inHighValue As Integer ) As Boolean
inLowValue <= inEvalValu <= inHighValue の時(範囲内)True
それ以外はFalse
isRange( inEvalVale As Integer , inRangePair As Pair ) As Boolean
inRangePair.Left <= inEvalValue <= inRangePair.Right の時(範囲内)True
それ以外はFalse
isSureTrue( inEvalString As String ) As Boolean
inEvalStringが "ON", "TRUE", "YES" , "UP" , "1"の場合にTrueを戻し、それ以外はFalseを戻す
−9時間は秒数にして−32400秒ですので、SecondsFrom1970は正しく-32400を戻していた訳です。(^o^)
大変、失礼しました。私がドキュメントを正しく解釈していなかった様です。
Var theDate As DateTIme = New DateTIme( 1970 , 1, 1, 0, 0, 0, 0, TimeZone.Current )
で SecondsFrom1970は−9時間を戻していました。
Var theDate As DateTIme = New DateTIme( 1970 , 1, 1, 0, 0, 0, 0, New TimeZone( "GMT" ) )
では、SecondsFrom1970は0となっておりました。
せっかちな物でドキュメントを良く読まないために余計な情報を記載してしまい。申し訳ありませんでした。
(まさかここを見ていると思ってなかった)友人から突っ込みがありまして、一部、補足・訂正致します。
○グローバルに関して
・Globalsと言うモジュール名が重要なのでは無くて、個々のプロパティなどのスコープが
Globalと指定されている必要があります。
ここでPrivateを指定すると、Globalsモジュールのプライベートとなってしまいます。
・グローバルの使用は最低必要限に留めるべきす。
○Original Class
・ArrayWarpperにあるmArray As Variantですが、Vatiantに関するドキュメントを参照して頂ければ
解ると思いますが、任意のオブジェクトを代入する事ができますので、基本的にList, Queue, Stackは
そのまま使って問題が無いと考えています。
これは、Variant型がどの型のオブジェクトを保持しているかを記録しているので、代入/参照時に
暗黙的に型変換を行っています。
ただ注意点として、今回のサンプルのようにオブジェクト型の配列を保持していると、直接の型変換が
出来ず、コンパイル時にエラーとなってしまいます。
このため、一旦、Variant型で参照して、その Variant型を使って、For Each等で繰り返しを処理する事で
回避しています。
○UserIFControlThread
・新規プロジェクトに利用する際に、Run イベントハンドラは基本的に変更する必要はありません。
・UserInterfaceUpdate()ハンドラを適宜、修正してください。
・多くのコントロールを扱う際には、関連するコントロールをコンテナにまとめて、そのコンテナの
メソッドで内包するコントロールだけの処理を行うべきです。
このコンテナのメソッドをUserInterfaceUpdate()から呼び出すと良いでしょう。
○DigitalClockContainer
・あえて説明しなかったのですが、このコンテナは前回のデジタルクロックアプリケーションを元に、
中心となるデジタルクロック表示をまとめて、コンテナにした物です。
このコンテナをウィンドウにレイアウトするだけで、デジタルクロック表示ができて、毎秒ごとの
画面更新も実行してくれる優れものです。
この様にクラスの独立性を高めて行く事で、デバッグ、テスト等の工数が減少し、再利用性が向上します。
OOPと言えども、作成する側がこの事を意識しないと、スパゲッティプログラムになってしまい。
後々の工程に影響します。
・他のクラス(オブジェクト)のメソッドやプロパティを参照・利用を極力しない。
→その様な場合はクラス設計が間違っている、あるいは、検討不足である事が多いので
一度立ち止まって考え直す。
・そもそも、1本のアプリケーションとして実装する必要があるのかも含めて、原点に返る事も
作業を単純化するためには必要である。
前回のデジタルクロックと今回のアラームアプリケーションは、独立したアプリケーションである事が
意味を持っています。
クロック表示するのは別の画面で分かりやすくしておいて、アラームは画面を表示しておく必要は
必ずしも無いのです。
お待たせしました。ポストしたのでこちらから取得してください。
ここから
Original Classフォルダ
ArrayWarpperクラス
配列を使用して1次元リスト構造を実装する基底となるクラス
Thread SafeになるようにメンバmCSでクリティカルセッションを管理する。
このクラスの親クラスは配列なのでXojoの組み込み機能でハンドリング出来る。
Listクラス
親クラスはListWrapperクラス
Listを実装したクラス。
Stackクラス
親クラスはListWrapperクラス
Arrayを用いて, Stack(LIFO:Last In First Out)を実装するクラス
Queueクラス
親クラスはListWrapperクラス
Arrayを用いて、Queue(FIFO:First In First Out)を実装するクラス
IntervalTimerクラス
一定時間ごとに、メソッドAction()を実行する。
時間間隔はミリ秒単位。
TimerクラスはAction()処理が終わってからperiodミリ秒を計測するので、一定間隔では
無くなります。このクラスは再度起動されるまでの間隔を再計算してPeriodに設定するので
比較的一定間隔での呼び出しが可能です。
Task, ThreadMgrフォルダ 試作実験中のクラス
DigltalClockContainer
ウィンドウ上にレイアウトして、デジタル時計を表示する。(24時間表示)
ウインドウにレイアウトして、デジタルクロックを表示する。
このコンテナだけでデジタルクロックが表示され、1秒毎に表示が更新される。
UIControlContainer
実装中のコンテナ。ウインドウのコントロールをアクティブ/非アクティブするコンテナ
DateLabelContainer
ウィンドウ上にレイアウトされ、今日の日付を表示する。
クリックするとトグル動作で、西暦と和暦を切り替えて表示する。
Xojoには和暦へ変換する機能が無いので、独自に実装しました。
1970年以降の昭和/平成/令和の日付に対応しています。
アプリケーションで使われるクラス
AlarmThread
アプリで管理されるアラームリストを調べて、現在時刻に合致すると、音声を再生する処理にデータを送る。
バックグラウンドで処理されます。
SoundEffectThread
AlarmThreadから送られた音声データ(テキスト文字列)をShellを経由してsayコマンドを起動する事で
音声を再生する。
バックグラウンドで処理されます。
UserIFControlThread
ボタンなどのコントロールをテキストフィールドの情報を元にアクティブ/非アクティブする。
個々のコントロールやテキストリストの選択や変更などを個別に処理をしていると、間違いや
設定漏れなどが発生するため、このThreadで一括処理しています。
コントロールが数個程度なら個別に処理しても適切に処理されると思われますが、
不具合の混入や、設定漏れなどの無いように一ヶ所にまとめました。
バックグラウンドで処理されます。
その他のコントロールはUI機能を実現するためのもので、特に拘った作り方をした訳では無いので
ソースコードを参照下さい。
GlobalSの使い方や、Thread処理の参考になれば幸いです。
スレッドはいまいち分かりにくい概念ですが、慣れると非常に強力なアイテムなので、
ぜひ習得される事をご検討下さい。
サンプルコードも別途、Githubへ登録します。
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
Threadクラスのサンプルコード アラーム.xojoproject
○機能
・ウィンドウを表示し、複数のアラームを時分秒の指定で登録出来ます。
・各アラームは鳴動させない事も出来ます。
・アラーム指定時刻になると、指定されたテキストメッセージを音声で再生します。
・音声の再生はバックグラウンドで処理されますので、時計の更新やUIの操作などには
影響がありません。
<注意>
・サンプルコードなので、以下の機能は未実装です。
1,登録したアラームの情報をファイルに保存する/ファイルから読み込む機能
2,内部での細かいエラーチェックや、エラー回復機能
3,その他細かい仕様上の詰めや、機能の実装等はありません。
○簡単な説明
Globalsモジュール
アプリケーション全体で使用する定数、メソッド、プロパティです。
Xojoでグローバルな変数や定数、メソッドを使いたい場合はGlobalsモジュールを定義します。
DateTimeFirstDay As DateTime
1970/1/1のDateTimeを返します。
isHour24Range( inData As Integer ) As Boolean
Integer型の引数が0〜23の間であればTrueを戻し、 それ以外はFalseを戻します。
isMinAndSecRange( inData As Integer ) As Boolean
Integer型の引数が0〜59の間であればTrueを戻し、 それ以外はFalseを戻します。
isRange( inEvalValue As Integer, inLowValue As Integer, inHighValue As Integer ) As Boolean
inLowValue <= inEvalValu <= inHighValue の時(範囲内)True
それ以外はFalse
isRange( inEvalVale As Integer , inRangePair As Pair ) As Boolean
inRangePair.Left <= inEvalValue <= inRangePair.Right の時(範囲内)True
それ以外はFalse
isSureTrue( inEvalString As String ) As Boolean
inEvalStringが "ON", "TRUE", "YES" , "UP" , "1"の場合にTrueを戻し、それ以外はFalseを戻す
xojo Rel.2024r3でThreadに新しい機能が追加されました。
ThreadクラスがPreemptive機能をサポートします。
デフォルトではCooperativeですが、IDEで設定を変更する事でPreemptiveに
する事が出来ます。
Preemptive threadsを使うにはいくつか注意点があります。
Preemptiveスレッドを作成するには、Type プロパティを Preemptive に設定します。
実行時にスレッドのタイプを変更できますが、スレッド内のTypeプロパティのみが変更可能です。
実行中の他のスレッドのTypeプロパティを変更しない様に注意が必要です。(してしまうと、クラッシュします)
また、ロック中(セマフォアやクリチカルセッション中)にTypeを切り替えるとクラッシュしたりロック
(フリーズ)します。
デフォルトでは、スレッドはcooperativeであり、全てがアプリと同じCPUコアで実行されリソースも
共有しています。
これは、実行すべき処理が多いほどスレッドが完了するまでに時間が掛かります。
Preemptiveスレッドはアプリ自体とは別のCPUコアで実行する事が出来ます。
そのため、CPUコアに余裕があれば、cooperativeスレッドよりも速く実行できる事です。
Preemptiveスレッドは、スレッド間やアプリとの間でメモリを共有しません。
つまり、スレッド内のコードはアプリの他のオブジェクト等のメモリにアクセス出来ません。
*個人的な見解ですが*、セマフォアやクリチカルセッションを使う事で
ArrayやDictionaryを扱える筈です。
その際はセマフォアやクリチカルセッションのTypeプロパティをPreemptiveにする必要が有ります。
これを忘れるとIllegalLockingExceptionになるようです。
xojoがサポートする機能のいくつかはPreemptiveスレッドから利用出来ません。
(この項目に関して、特定の情報は見つけられなかったです。)
cooperativeスレッドと同様に、UIに直接アクセス出来ません。
Runtime.IterateObjectsメソッドとXojoScriptは、Preemptiveスレッドで安全に使用できません。
閑話休題
とっても面倒くさい様な話を降り出したのですが、何かおかしいぞと思ったアナタ!
ヨクゾ気付きました。エライ、エライ、 なでなでしてあげます(^o^)
昨今のパソコンはCPU等が高機能化し、4コアや8コア程度は当たり前、お金を出し惜しみしなければ
32コアとか128コアとかのCPUを選ぶ事も出来ます。
但し、WindowsのクライアントOSは32コアを超えるCPUコアはサポートされず、使用されません。
であるのであれば、そんな時間のかかる場合はコンパイラやOSが適当に割り振ってくれても
良いのでは無いかと思いませんか?
まぁ、そう考えるのが普通ですな!
でも、現状のOSやコンパイラは、
・どの部分が時間のかかる処理か
や
・どの部分が別のコアに割り当てても問題が無いか
を判断する事がいまだ出来ていません。
ですので、プログラムを記述する我々が適切に明確な指示をしない事には
プログラムの分離はできないのです。
21世紀になったら、どら衛門や100万馬力のロボットが出来ていて、プログラミングから開放されると
夢見ていた自分を殴ってやりたいです。😂
〜〜蛇足〜〜
確かにコンパイラの最適化は進歩しました。インテルのコンパイラなどはベンチマークテストのコードを
何事もなかったかの様に処理して、ほとんど空のコードを吐き出して、唖然としました。
昔々、パソコンがまだ一般的じゃなかった頃、会社で導入したコンピュータでとあるシュミレータを
作って居た頃でも、並列化の部分はゴリゴリと手作業で書いていました。
WindowsもmacOSもAPIやライブラリは豊富にそろっていますが、基本的な部分はIBMのOS/370や
DECのVMSとそうは変わっていないように思えます。
ですので、若い方々の努力の参考になれば幸いです。<m(__)m>
XojoのThreadクラスを知っていますか?
Threadクラスと聞いても、何の事かも判らないし、得体の知れない物と思われているかも判りませんが、
簡単な例を上げてみると:
通常のプログラムは
1)ユーザから何かの入力を受けて、加工を施してから画面に表示します。
また
2)ファイルやネットワークからデータを読み込んで、加工して画面に表示したり、
別のファイルに書き出したりします。
この様な場合に、加工すべきデータが数十〜数百の程度だと現状のパソコンでも、
処理内容にも拠りますがほぼ数秒以内に結果が得られます。
しかし
加工すべき元データが数万〜数千万の単位になると、数分もしくは数時間掛かるかも知れません。
その作業時間にユーザからのアクションである各種イベントが処理されなくなると、どうなるでしょうか?
・プログラムが暴走orハングアップしたのか?
・何か間違った捜査をしてしまったのでは無いか?
・指定すべきデータを間違ったのでやり直したい!
等と言う状況になるかも…
そのような場合に役立つのが、Threadクラスや別途説明するWorkerクラスです。
(今回はThreadクラスの説明ですので、Workerクラスは別の機会にします)
加工処理に時間がかかる場合、ユーザに状況を把握してもらうため
・ダイアログウィンドウ等を表示して、全データ件数の内の何件目を処理しているのかを表示する。
・ユーザからの途中での停止、中断等のアクションを受けて、適切な対応をする。
みたいな動作をする事が望ましいと考えられます。
そこで、アプリケーションの本来の流れはダイアログウィンドウを表示し、データ加工の進行状況を
更新しつつ、イベントを適切に処理しておく事で、ユーザはいつでもアプリケーションを安心して
使用できます。
本来の流れとは別に(Threadクラスを用いて)データ加工を順次行う事で、プログラムの流れを分離して
簡潔な構成にして、作成が容易になり、バグの発生も軽減できる事が期待できます。
但し、Threadの処理内ではThread SafeなAPIしか呼び出す事が出来ません。
具体的には画面への表示更新などはThread Safeでは無いので、呼び出す事は出来ません。
ご注意ください。
xojoの日本語フォーラムもありますが、最近はほとんど使われていないようです。
xojoのオープンソースなプロジェクトがいくつかあるそうなので、xojo.comのドキュメントサイトも見てください。
https://documentation.xojo.com/resources/third_party/open_source_projects.html