MASATOの開発日記 2000年5月


5/29

 またもやだいぶ空いてしまったが、前回に引き続いてJavaの話題。JavaAppletの動作確認には、Netscape4.7とIE4SP1を使っているのだが、これがまた動作が全然違う。Netscapeの方が、Appletのロード時間が非常に長いIEの5倍くらいあるのでは無いだろうか。恐らく何らかのセキュリティの為コードチェックをかけているのではないかと予想しているのだが、自分のAppletをデバッグする際にはとことん邪魔である。IEでは、TextFieldが1文字入力するたびにちらほら点滅してうっとおしい。再描画を必要無しに行っている感じだ。Netscapeはそういった事は無くWindowsのEditBoxのように扱えるのだが、なんとNetscapeはブラウザのサイズを変えるとReloadしてくれる。画面が狭いな見にくいな、と思ってちょっと広げたらReload。Appletで言うと、stopされてstartされるわけだ。それまでの作業が全部ぱぁヽ(´ー‘)ノ。

 それで、今回の話題は、Objectクラスの関数のwait&notify関数の使い方である。これは、マルチスレッドを扱う時しか必要としない。

(VisualCafe)wait&notifyの使い方。

 wait&notifyは、生産者&消費者の問題を扱う時に必要とされる。例えば、データー受信スレッドとデーター処理スレッドという二つのスレッドがあったとする。データー受信スレッドは、ひたすらどこかからデーターを受信するだけである。これが生産者となる。データー処理スレッドは、受信スレッドが行ったデーターをひたすら処理するだけである。これが消費者である。消費者が全て消費してしまった場合(例、処理スレッドが全てのデーターを処理してしまった場合)、生産者が生産を行うまで(例、受信スレッドがデーターを受け取るまで)待たなければならない。Threadのsuspend&resumeでも出来るだろうが、ちょっとややこしいし、安全で無いので推奨されていないとされているので、あまりお勧めできない。なお、待機するのに無限ループに入る、といった方法はご法度である。何もしないのにCPUパワーをひたすら消費する必要は無い。

 なんか前置きが長くなってしまった。そもそもこのwait&notifyという関数自体使い方が分かり難いものなのでしょーがないだろう。一応前もって書いておくが、以下の方法が唯一の方法では無い。wait&notify関数は、Objectクラスのメソッドなので、どのオブジェクトに対しても呼び出せるので、色々なコードが書けるだろう。

 例えば、消費&生産用バッファクラスは以下のように書ける。
-----
public class ObjectBuffer
{
    protected Vector bufVector;
    public ObjectBuffer()
    {
        bufVector = new Vector();
    }

    public synchronized void add(Object obj)
    {
        if (bufVector.isEmpty())notify();
        bufVector.addElement(obj);
    }

    public synchronized Object get()
    {
        if (bufVector.isEmpty()){
            try {
               wait();
            }catch (InterruptedException e){
            }
        }
        Object obj = bufVector.elementAt(0);
        bufVector.removeElementAt(0);
        return obj;
    }
}
-----
 注意するのは、this対するnotifywaitは、synchronized関数の中に無ければならない。あるいはsynchronized(this){}の中にあるかだ。そうしないと例外が出る。私は、この制限が、waitやnotifyが同時に呼ばれたら困るから、と解釈している。それだったらそもそもwaitnotify自体をsynchronized関数にすれば良いと思うのだが・・・。

 生産者にあたるものは、上記のDataBufferクラスに対してaddを呼び出し、消費者にあたるものはgetを呼び出す。getを呼んで、バッファが空になっていたら、その時点で消費者が動いているスレッドは待機する。この状態だとCPUパワーはほとんど必要無いはずだ。生産者がなにかaddした時点で、notifyが呼び出され、消費者スレッドが再開される。なお、これは一つの生産者と一つの消費者のためのバッファクラスである。複数の生産者及び消費者がいる場合は、notifyAllを使う必要がある。(他にも微細な変更が必要)
 

5/16

 MFCのとあるアプリケーションがひとまず完成したので、Javaでの開発にうつる。(まだ開発途中のMFCアプリケーションはいっぱいあるけどね)
 Javaでの開発は、VisualCafe3.0cを用いている。これは、Symantecオリジナルコンパイラを用いているようで、恐ろしくコンパイルが早い。当社比JDKの10倍である。VisualCafe3.0cには、全角文字で検索をかけると強制終了するとか、Java2を使うにはちょっと手間がかかるといった細かい問題があるのだが、このコンパイルの早さで全てを補っている。そういえばVisualCafe4が出てたなぁ・・・。ちょっと欲しいとか思いました。

(Visual Cafe)VisualCafeのエディタで、タブをスペース4つでは無く、タブとして扱う方法。

 VisualC++では最初からタブはタブとして扱われている。表示される時は、タブ間隔が4文字分となっていてなかなか見やすい。これと同じ表示方法をVisualCafeでやるにはどうしたら良いのだろうか。一見簡単なように見えるが、ちょっと見落としやすい方法なので書いてみることにする。
  1. メニューの「ツール」->「環境設定」、でダイアログを開く。
  2. 「書式」を選び、「ファイルの拡張子別の設定」からjavaを選択する。このリストボックスが小さいため非常に見落としやすいが、ちゃんとスクロールさせていけばしっかり見つかるはずである。
  3. あとは、「タブをスペースに展開」、のチェックを外すだけである。
 これで終わりだ。見つかってしまえば簡単。正直いってこれが簡単に見つからないのはVisualCafe3.0cのユーザーインターフェースが悪いと思う。
 ちなみに、たぶんこれが最初の言語やライブラリとは関係の無い、エディタに関してのトピックだろう。
 

5/10

 MFCでダイアログ等を作ったときに、そこに似たような機能を持った大量のボタンやチェックボックスを作りたい、と思ったことは無いでしょうか?
大量といっても、50個とか訳が分からなくなるくらいの数では無く、5個や10個程度の物です。RPGの戦闘シーンなんかでは「攻撃」「防御」「魔法」「逃げる」といったボタンを作ったりしたくなるかもしれません。住所録等では、マーク1、マーク2、マーク3、マーク4、マーク5といったチェックボックスを作り、後でマーク1がONであるものだけ印刷、といった機能をつけてみたくなるかもしれません。こういった、似たような機能を持った複数のボタン等が押された事を判断するには、普通にクラスウィザードを使っただけでは、10個のボタンに対して10個のメンバ関数を作らなくてはなりません。別に10個作っても大した問題は無いのですが、これを一つの関数で受け取って、その関数でn番目のボタンが押されました、と判別できれば結構便利そうです。

(Visual C++)複数のコントロールからのメッセージを一つの関数で受け取る方法。

 例題として、複数のボタンから送られてくるBN_CLICKEDメッセージを扱ってみます。ボタンの数は10個で、IDは、IDC_BUTTON1から、IDC_BUTTON10までとします。VC++は、コピー&ペーストでボタンを増殖させると、勝手に最後の数字を一つ上げてくれるから楽ですね。では、以下に手順を示します。
  1. まず、シンボルブラウザを用いて、IDを全て連番にします。VC++のメニューの表示→シンボルブラウザ、で使えます。連番になっていれば何でも良いのですが、将来ボタンの数が増える事も考え、他のIDとはちょっと離れた数字にしましょう。例えば、2000〜2009としても良いですね。(もちろん他のIDと重なるのは危険です。気をつけてください。)

  2.  
  3. お次は、クラスウィザードを用いないで自力でメッセージマップを書きます。追加する場所は、cppファイルの、BEGIN_MESSAGE_MAP(..., ...)と書いてある行と//{{AFX_MSG_MAPと書いてある行の間です。そこに以下の1行を追加して下さい。

  4. -----
    ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON1, IDC_BUTTON10, OnButtonRange)
    -----
    最後にセミコロンはいりませんので注意。
    IDがIDC_BUTTON1からIDC_BUTTON10であるコントロールから送られて来るBN_CLICKEDメッセージを、OnButtonRange関数で受ける、という意味です。関数名等は場合に応じて変えて下さい。
     
  5. 最後に、メンバ関数を追加します。関数の型は以下の通りです。

  6. -----
    afx_msg void OnButtonRange(UINT id);
    -----
    関数定義部は次の様に書くと良いでしょう。
    -----
    void Cxxx::OnButtonRange(UINT id)
    {
        int n = id - IDC_BUTTON1;
        // これで、nに押されたボタンの番号が入ります。あとは好きなように処理しましょう。
        // ...
    }
    -----
    これでどのボタンを押してもOnButtonRangeが呼び出されます。
BN_CLICKEDでは無く他のメッセージにすれば、様々なコントロールに応用が効くでしょう。ただし、コントロール数が多くなりすぎて人間に理解できないダイアログになってしまわないように注意してください。(^^)



戻る
トップページへ
masato@mb.kcom.ne.jp