Multithreaded Application Tutorial/ja

From Lazarus wiki
Jump to navigationJump to search

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) polski (pl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆)‎ (zh_CN)

日本語版メニュー
メインページ - Lazarus Documentation日本語版 - 翻訳ノート - 日本語障害情報

概要

このページはいかにしてこれからFree PascalとLazarusを使ってマルチスレッドアプリケーションを書いたりデバッグするかということについて書かれています。

マルチスレッドアプリケーションは、2つかそれ以上のスレッドを作成、同時実行して作業を行うものです。

メインスレッドと呼ばれる一つのスレッドがあります。メインスレッドはオペレーティングシステムによって生成され、自分のアプリケーションの始動すると生成されます。 メインスレッドはかならずひとつのスレッドである必要があり、ユーザーインターフェイス、コンポーネントを更新する唯一のスレッドです(そうでないとアプリケーションは暴走します)。

大元の考え方として、アプリケーションは処理をバックグラウンドで行うことができます(二つ目のスレッドの中で)。その間、ユーザーは仕事を続けることができます(メインスレッドを使って)。

ほかのスレッドの使いかたとして頻繁に反応が必要なアプリケーションがあります。例えばあなたがアプリケーションをつくって、ユーザーがボタンを押して、アプリケーションが処理を開始したとします(すごく重たい処理です)。処理の間、スクリーンは凍りつき反応が無くなり、ユーザーはアプリケーションが落ちたと思うでしょう。よくないことです。もし、その重たい処理を2つ目のスレッドの中で実行したらアプリケーションは反応を返し、まるでそんな処理は行っていないかのように振る舞います。スレッドを開始する前にフォームの処理を開始するボタンを使用不可能にしておけば、ユーザーがそれ以上の仕事を要求することを無くすことができ、名案です。

また、サーバアプリケーションを作る際に、大量のクライアントに返答しなければならない場合にも使えます。

TThreadクラス

このサンプルは「examples/multithreading/」にあります。

マルチスレッドアプリケーションはTThreadクラスを使用すると簡単に作成できます。 このクラスは簡単に追加のスレッド(メインスレッドと並列に実行する)を作ることができます。

そのためには通常、Createコンストラクタと、Executeメソッドの二つのメソッドをオーバーライドしなければなりません。

コンストラクタは、あなたが走らせるスレッドを準備する際に使います。あなたは値と変数もしくはプロパティに初期値を納める必要があります。TThreadのオリジナルのコンストラクタはSuspendedというパラメータを必要とします。 SuspendedがTrueの場合、スレッド作成された後、自動的にスレッドは実行されます。 SuspendedがFalseの場合、スレッド作成された後、直ちにスレッドは実行されます。 スレッドがSuspendedで作成されれば、Resumeメソッドが呼ばれた後に実行されます。

FPCのバージョンが2.0.1以降の場合、TThread.CreateはStackSizeという暗黙のパラメータを持っています。 あなたは必要ならば自分の作るスレッドのデフォルトスタックサイズを変更することが可能です。 スレッド内でディープな手続きを呼び出す場合には重宝するでしょう。 あなたがスタックサイズを指定しなければ、OSによる規定のスタックサイズが使用されます。

あなたはオーバーライドされたExecuteメソッド内にスレッドで実行されるコードを書きます。

TThreadクラスはひとつの Terminated : boolean; という重要なプロパティを持っています。

TerminatedがTrueであって、スレッドがループを持っている場合(これは普通です)、ループは終了されます(デフォルトではFalseです)。したがってどんなサイクルもTerminatedがTrueであるかチェックする必要があり、また速やかにExecuteメソッドを終了させることが可能なように、必要なクリーンアップが終わった後にしなければなりません。

ですからTerminatedメソッドではデフォルトではなにもしないことを、覚えておいてください。Executeメソッドにはメソッドがメソッドを終了するためのサポートを必ず実装しなければなりません。

先に説明したように、スレッドは可視コンポーネントになにかを行うべきではありません。なにかをユーザーに示すためにはメインスレッドで行います。 これを行うためにTThreadにはSynchronizeというメソッドがあります。 このメソッド(パラメータなし)により、連動することが可能です。 あなたがSynchronixe(@MyMethod)として、メソッドを呼び出すとき、スレッドの実行は一時停止されます。そしてMyMethodがメインスレッドで行われた後、スレッド実行が再開されます。

他にもTThreadには重要なプロパティがあります。 FreeOnTerminateはこのプロパティがTrueの際、スレッドオブジェクトはスレッドの実行(Executeメソッド)が停止した後に開放されます。そうでないばあいは、アプリケーションは手動でオブジェクトを開放する必要があります。

例:

 Type
   TMyThread = class(TThread)
   private
     fStatusText : string;
     procedure ShowStatus;
   protected
     procedure Execute; override;
   public
     Constructor Create(Suspended : boolean);
   end;
 constructor TMyThread.Create(CreateSuspended : boolean);
 begin
   FreeOnTerminate := True;
   inherited Create(CreateSuspended);
 end;
 procedure TMyThread.ShowStatus;
 begin
   Form1.Caption := fStatusText;
 end;

 procedure TMyThread.Execute;
 var
   newStatus : string;
 begin
   fStatusText := 'Starting...';
   Synchronize(@Showstatus);
   fStatusText := 'Running...';
   while (not Terminated) and ([any condition required]) do
     begin
       ...
       [here goes the code of the main thread loop]
       ...
       if NewStatus <> fStatusText then
         begin
           fStatusText := newStatus;
           Synchronize(@Showstatus);
         end;
     end;
 end;

アプリケーション側では,

 var
   MyThread : TMyThread;
 begin
   MyThread := TMyThread.Create(True); // This way it doesn't start automatically
   ...
   [Here the code initialises anything required before the threads starts executing]
   ...
   MyThread.Resume;
 end;

特別に注意すべきこと

Windowsで-Ctスイッチ(スタックチェック)をつかうスレッドには頭の痛い問題があります。 理由は定かではありませんが、もしデフォルトのスタックサイズを使う場合、TTread.Createでスタックのチェックがおそらくトリガーとなって発生します。 現在の運用上の回避策としては -Ctスイッチを使わないことです。これでメインスレッドで例外をひきおこさなくなります。しかし、新しいスレッドをつくったときに発生することがあります。 これは、スレッドが絶対開始できないように思えます。

スレッド生成時に発生するその他の例外や、これをチェックする良いコードです。


    MyThread:=TThread.Create(False);
    if Assigned(MyThread.FatalException) then
      raise MyThread.FatalException;

このコードはスレッド生成時に発生するどんな例外でも、メインのスレッドでraiseするように確認します。

マルチスレッドアプリケーションで必要なユニット

Windowsでは特に気をつけることはないのですが、LinuxやMaxOSX,FreeBSDでは、かならずプロジェクトのユニット(つまり、プログラムユニット、.lpr)で、cthreadsユニットをusesする必要があります。

Lazarusアプリケーションのコードでは次のようになるでしょう。

 program MyMultiThreadedProgram;
 {$H+}
 uses
 {$ifdef unix}
   cthreads,
 {$endif}
   Interfaces, // this includes the LCL widgetset
   Forms
   { add your units here },

SMP Support

The good news is that if your application works properly multithreaded this way, it is already SMP enabled!

Debuging Multithreaded Applications with Lazarus

The debugging on Lazarus is not fully functional yet. But, if you try to debug a multithreaded application on Linux, you will have one big problem: the X server will hang.

It is unknown how to solve this properly, but a workaround is:

Create a new instance of X with:

 X :1 &

It will open, and when you switch to another desktop (the one you are working with pressing CTRL+ALT+F7), you will be able to go back to the new graphical desktop with CTRL+ALT+F8 (if this combination does not work, try with CTRL+ALT+F2... this one worked on Slackware).

Then you could, if you want, create a desktop session on the X started with:

 gnome-session --display=:1 &

Then, in Lazarus, on the run parameters dialog for the project, check "Use display" and enter :1.

Now the application will run on the seccond X server and you will be able to debug it on the first one.

This was tested with Free Pascal 2.0 and Lazarus 0.9.10 on Windows and Linux.