FreeBASIC が提供する組み込みサポートを使った
Multi-threading プログラミング。
序文:
Multi-threading プログラミングにより、並行処理フローを生成できます。
マルチ・プロセッサ・システムまたはマルチ・コア・システムで最も効果的です。処理フローを、別のプロセッサ/コアで実行するようにスケジュールできるため、並列処理または分散処理によって速度が向上します。
マルチプロセッサまたはマルチコアシステムで最も効果的ですが、シングルプロセッサまたはシングルコアシステムでも、I/O およびプロセス実行を停止する可能性のある他のシステム機能の、待ち時間を活用する利点があります。
1つのスレッドが実行されている間に、別のスレッドが I/O またはその他のシステム待ち時間を待機している場合があります。
スレッドは新しいプロセスを生成するよりもオーバーヘッドが少なくて済みます。システムはプロセスの新しいシステム仮想メモリ空間と環境を初期化しないためです。
プロセス内のすべてのスレッドは、同じアドレス空間を共有します。
定義
マルチスレッド・プロセスには、同時に実行できる 2つ以上の部分が含まれています。
このようなプログラムの各部分は、スレッドと呼ばれ、各スレッドは、実行の個別のパスを定義します。
プロセスとスレッドの違い:
- プロセス:
直観的には、プロセスは、メモリに配置されたプログラム、またはそれに関連付けられたすべてのランタイム環境(またはすべてのリソース)で実行されている、プログラムにすぎません。
つまり、プログラムがマシンのハードディスク上にある場合、プログラムは常にプログラムまたは単純なアプリケーションです。しかし、一度実行される(またはメモリに配置される)と、リソースのセット(使用されるメモリ空間の量、使用されるプロセッサレジスタとそのステータス、プロセスの所有者、許可された操作など)が割り当てられ、その名前が変更されます。
これがプロセスになります。
簡単にするために、プログラムの実行で、韻を踏ませます。
- スレッド:
スレッドは、プロセスの部分または一部であり、オペレーティング・システムのスケジューラによって、独立して処理される命令の、最小の連続ユニットです。
これは、プロセスに含まれる一連の命令で構成される、実行または処理ユニットです。
スケジューラーが管理できるプロセス内の、最小のタスクまたは操作と見なします。
スレッドは、データセグメント、コードセグメントなどの情報を、共有します。独自のレジスタ、スタックなどを含む、同じベーススレッド(以下を参照)によって生成されたピアスレッドも合わせてです。
明らかに、プロセスは1つ以上のスレッドで構成でき、すべてはプログラマーに依存します。
シングルスレッド・プロセスの場合、マルチスレッドの反対のシングルスレッドについて話します。
スレッドは、並列処理によってプログラムを改善する、一般的な方法です。
マルチスレッド・プログラミング
シングルスレッドのプログラムは、一度にコードの1行を実行してから、順番に次の行に移動します(分岐、関数呼び出しなどを除く)。
これは通常、プログラマーがコードを記述するときの、デフォルトの動作です。
スレッドを作成する主な理由はいくつかあります。例えば:
-
完了するのに長く時間がかかるタスクを実行する必要があっても、ユーザーに完了まで待たせたくありませんよね。
これは、タスクの並列処理と呼ばれます。
スレッドを作成する目的は、実際にタスクの実行を高速化するのではなく、アプリケーションを、ユーザーインターフェイスで、応答性を改善することです。
重要なのは、この設計パターンを有効にするには、タスクが、暗黙のメインスレッドからほとんど独立して実行できる必要があることです。
- 複雑なタスクに、塊に分割されることで、パフォーマンス上有利になる可能性があるものがあります。
ここでは、複数のスレッドを作成して、それぞれがタスクの 1つの部分に専念するようにします。
すべてのピースが完了すると、メインス・レッドは、サブ結果を最終結果に集約します。
このパターンは、並列集約パターンと呼ばれます。
これが機能するには、タスクや計算の合計結果の一部を、単独で計算できる必要があります。後続する結果の部分が、先行する部分に依存してはだめです。
マルチスレッド・プログラムは、同時にプログラム内の2つ以上の場所から実行されます(少なくとも、同時に実行されているように見えます):
- 順序の概要:
プログラムが起動すると、すでに 1つの暗黙的なスレッドが、すぐ実行開始します。
これは通常、プログラムの「メイン」スレッドと呼ばれます。これは、プログラムの開始時に実行されるスレッドだからです:
- メインスレッドは、プログラマが、他の "子" スレッドを生成するスレッドです(スレッド自体が、他の "子" スレッドを生成する場合があります)。
- メインスレッドと他のスレッドは、同時に実行されます。
- 一般に、 "親" スレッドは、子スレッドの結果を待つ必要があるため、関連するスレッドが終了するのを待ちます。
- 多くの場合、メインスレッドは、さまざまなシャットダウン・アクションをするため、実行を終了する最後のスレッドである必要があります(同様に、"子" スレッドは、最終的に生成される "サブ-子" スレッドに関してそうする必要があります)。
- しかし、それ以外に、暗黙のメインスレッドは、プログラマが明示的に生成した他のすべてのスレッドと競合することができます。
- マルチ-プロセッサまたはマルチ-コア・システムでのスレッド化:
システムに、マルチ-プロセッサまたはマルチ-コアのキャパシティがある場合、新しいスレッドを作成すると、新しいスレッドは、未使用または最も使用率の低いプロセッサ/コアで実行される場合があります。
アプリケーションが、マルチ-スレッドだと、ほぼ確実に、複数のプロセッサ/コアを自動的に利用できます。
対照的に、シングルプロセッサ/コアでは、スレッドは、オペレーティング・システムによって、プロセスがマルチタスク環境と同じ方法でタイムスライスされ、すべてがシングルプロセッサ/コアで実行されるため、効果的なパフォーマンスの向上はありません。
注:
- たとえば、4つのスレッドを作成しても、必ずしもパフォーマンスが 4倍向上するわけではありません。
使用中のアルゴリズム、スレッド間の調整量、スレッド間で共有されるメモリへのアクセスによるオーバーヘッド、およびその他の要因に応じて、速度の増加は、準線形になります。
- 順次(シングル・スレッド)処理のために適切に設計されたアルゴリズムは、必ずしもマルチスレッド用に最適化されているわけではなく、大幅に再設計する必要があります。
- スレッドセーフ:
プログラムのいくつかの部分を一度に実行すると、予期しない動作が発生したり、予防措置が考慮されていない場合には、メモリの破損やクラッシュが発生することがあります。
同じ手続きが互いに影響を与えることなく複数のスレッドで同時に独立して実行でき、さらに、変数にアクセスまたは変更するときに、他のスレッドと連携するように設計されている場合、その手続きは、スレッドセーフである、と言われます。
スレッドセーフコードを作成することは、マルチスレッド・プログラミングを理解するプログラマにとって、本質的に最大の問題であり、障害となります。
経験豊富なプログラマでさえ、理解やデバッグが非常に難しい、捉えにくく危険なバグに出くわすことがあります。
- 同期オブジェクト:
同期オブジェクトは、あらゆる種類のイベントを通知することにより、スレッドが相互に通信できるようにします。
たとえば、リソースにアクセスしていること、およびリソースを使用する他のスレッドが問題を回避するために待機する必要があることを示します。
最も一般的に使用される同期オブジェクトは、マルチスレッド・プログラムの危険領域(critical section) で使われる、「相互排除 "mutual exclusion"」と「条件変数 "conditional variables"」で、不可分操作(atomic action)として実行される必要があるコード・ブロックです(同様の動作を実行する他のスレッドとの同時性はありません)。
マルチ-スレッドは非常に危険であり、ここでの間違いは非常にコストがかかります。
問題が見つかった場合、デバッグは困難または不可能ですが、最悪というわけではありません。
最悪なのは、コードに間違いがあるのに、それなりに機能する場合です。
これは、スレッドが使われると、実行フローが確定的ではなくなるために、発生する可能性があります。
新しいスレッドを作成するのに、どれくらい時間がかかりますか?
同時スレッドはいくつありますか?
CPU時間は、スレッド間でどのように分配されますか?
他のスレッドも実行できるように、タイム-スライスを解放して、各スレッド(メイン・スレッドを含む)の各
'For' ループに常に
'Sleep x, 1' テンポを設定することを、推奨します。
これらの要素はすべて、実行全体に影響します。
マルチ-スレッド・コードを書くことは、何に対しても準備することであり、それは危険で興奮させることです。
マルチ-スレッドの組み込みサポート
FreeBASIC 組み込みスレッド関数が使用される場合、スレッド-セーフ・ランタイム・ライブラリが自動的に使われます(したがって、リンカーの '-mt'コンパイラ・オプションは、プログラマが独自のスレッド・ルーチンを使う場合にのみ必要です)。
このセクション(「マルチ-スレッド」)の以下のページで説明します:
参照