試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

Vala言語で外部プロセスを実行する(スレッドを使用してGTK+のテキストビューに実行結果を表示・メモ)

ここでは「外部プロセスをバックグラウンド実行しつつ、出力をGTK+のテキストビューにリアルタイム表示する」でPythonを用いて行ったように、GTK+のテキスト入力欄に入力したコマンドを実行し、結果をテキストビュー上に表示するコードを作成した上でのメモを行う。

スレッドを使用する

子プロセスの出力を読み込んで処理しているときにGUIを固まらせない(メインループ上で処理しない)ようにするためにはPythonで行ったときと同様にスレッドを用いるのが便利で、Vala言語では
http://live.gnome.org/Vala/ThreadingSamples
のサンプルを参考にできる。

  • 別スレッドの関数は戻り値の型を「void *」にする
  • GLib.Thread.create()でその関数を指定し、必要に応じて戻り値の(GLib.Thread)オブジェクト(「weak GLib.Thread」と弱い参照にする)についてメンバ関数を呼び出して同期(メンバ関数join())などの制御を行う・同期可能にするには2番目の真偽値をtrueにする
  • コンパイル時には--threadオプションを指定

スレッド機能が使用できない場合はGLib.Thread.supported()がfalseを返すので、メイン関数上でチェックするようにするとよい。

public static int main (string[] args)
{
  if (! GLib.Thread.supported ())
  {
    GLib.error ("This program needs threading support.\n");
    return 1;
  }

  (スレッド使用時の処理...)

  return 0;
}

スレッドを使用しない場合でもIOチャンネル(「Vala言語で外部プロセスを実行する(GLib.Process.spawn_async_with_pipes()を使用・メインループを用いない場合と出力メッセージの処理)」で扱っている)の監視機能を用いることである程度はGUIを固まらせないようにできるのだが、ここでは扱わない。
スレッドの詳しい扱い方はGLibのC言語のリファレンスなどが参考になる。
ここでは標準出力と標準エラー出力の処理にスレッドを用いるため、特にスレッドの同期を行ったりすることなどはしない。

別スレッド上でGUIに関する処理を行う場合の注意

PyGTKと同様、GTK+GUIに関する処理を別スレッドの関数の中で行う場合は

  • Gdk.threads_init()Gtk.main()より前に呼ぶ
  • アプリケーションが(再現性なく)落ちるのを防ぐため、別スレッドの関数の中でGTK+GUIに関する処理を行う開始位置にGdk.threads_enter()、終了位置にGdk.threads_leave()を記述

となる。

return文がありません?

手元で作成したコードをコンパイルしようとすると

[ファイル名].vala:[開始位置]-[終了位置]: error: missing return statement at end of method body
    public void *[関数名] ()
    ^^^^^^^^^^^^^^^^^^^^^
Compilation failed: 1 error(s), 0 warning(s)

となってしまった。
上のサンプルのURLにあるサンプルコードでは別スレッドで実行される関数にreturn文はなく、そのサンプルコードをコピペしてコンパイルすることもできたのだが、自分で作成したコードでは(同じように書いているはずなのに)このようにエラーが出た。
色々試してみた結果、別スレッドで実行される関数の最後に「return null;」を書くことでエラーは回避でき、正常に動作もすることが分かった。

まとめると

スレッド関係の部分のみをまとめると下のような形になる。

void *threadfunc ()
{
  (処理...)

  Gdk.threads_enter ();
  (GTK+上の描画に関する処理...)
  Gdk.threads_leave ();

  (処理...)

  return null;
}

void func ()
{
  weak GLib.Thread t;
  try
  {
    t = GLib.Thread.create (threadfunc, true);  // 2番目の引数はjoin()しなければfalseでOK

    (必要に応じてt.join()で同期)
  }
  catch (GLib.ThreadError e)
  {
    GLib.warning ("ThreadError: %s", e.message);
  }
}

(「Vala言語で外部プロセスを実行する(スレッドを使用してGTK+のテキストビューに実行結果を表示・コード例)」に続く)

関連記事:

参考URL:

使用したバージョン:

  • Vala 0.7.4, 0.7.5