hakeの日記

Windows環境でプログラミングの勉強をしています。

並行処理 その2

Rubyの勉強

昨日の続き、こんどは一方のメソッドの出力をもう一方のメソッドが利用する例を試してみる。
メソッドcnt3は配列@aに0〜99の数値を順番に0.1秒間隔で代入、メソッドcnt4は@aの要素を順番に全て表示させる。これらのメソッドを各々ボタン1とボタン2に割り付け、ボタン1→ボタン2の順に押す。
メソッドcnt3がスレッド処理されていなければ、問題なく0〜99の数値は表示される。メソッドcnt3の3ヶ所の#を削除してスレッド処理させるようにすると、メソッドcnt4は99まで表示することなく終了してしまう。これは両メソッドが同時に処理され、かつメソッドcnt4の方が速く処理される為に未だ値を代入されていない@a[i]のnilを検出してしまう為である。それぞれが独立して動作しているのだから当然といえば当然。


これでは困るので、メソッドcnt4の代わりにメソッドcnt5をボタン2に割り付けてみる。

#  connect(@pb2,QSIGNAL("clicked()"), self, 'cnt4')
   connect(@pb2,QSIGNAL("clicked()"), self, 'cnt5')

メソッドcnt5は、メソッドcnt3が実行中であることを示す変数@execCnt3をチェックして、メソッドcnt3が実行中であればa[i]の内容がnilであっても終了せずにウェイトしている。こうすれば0〜99までの数値が問題なく表示されることになる。
メソッドcnt5のuntil文はcnt3が終了した時点で表示されていない数値を表示させる為の処理。

(補足)こういう場合は、@a=Queue.newとして、@a.pushと@a.popを使用したほうが正解らしい。

#!/usr/bin/env ruby

require "qte"
require "qpe"
require "thread"
include Qte
include Qpe


class SampleWindow < QMainWindow
   def initialize()
      super()
      setCaption(tr("サンプル"))
      @msg = QLabel.new(tr("これはサンプルプログラム"),self)
      @msg.setGeometry(10,10,300,30)

      @ebox1 = QMultiLineEdit.new(self)
      @ebox1.setGeometry(0,100,635,300)
      @ebox1.setReadOnly(true)

      @pb1 = QPushButton.new(tr("ボタン1"),self)
      @pb1.setGeometry(320,5,100,30)
      connect(@pb1,QSIGNAL("clicked()"), self, 'cnt3')

      @pb2 = QPushButton.new(tr("ボタン2"),self)
      @pb2.setGeometry(430,5,100,30)
      connect(@pb2,QSIGNAL("clicked()"), self, 'cnt4')
#      connect(@pb2,QSIGNAL("clicked()"), self, 'cnt5')

      @a = Array.new
      @execCnt3 = false
   end

   def cnt3
#      t = Thread.new do
#         Thread.pass
         @execCnt3 = true
         @ebox1.insertLine("cnt3 start")
         for i in 0..99 do
            @a[i] = i
            sleep 0.1
         end
         @ebox1.insertLine("cnt3 end")
         @execCnt3 = false
#      end
   end

   def cnt4
         @ebox1.insertLine("cnt4 start")
         @a.each do |i|
            @ebox1.insertLine(i.to_s)
         end
         @ebox1.insertLine("cnt4 end")
   end

   def cnt5
         @ebox1.insertLine("cnt5 start")
         i = 0
         while @execCnt3
            if @a[i].nil? then
               sleep 0.5
            else
               @ebox1.insertLine(@a[i].to_s)
               i = i + 1
            end
         end
         until @a[i].nil?
             @ebox1.insertLine(@a[i].to_s)
             i = i + 1
         end
         @ebox1.insertLine("cnt5 end")
#         QMessageBox.information(self, "End", "End")
   end
end

$defaultCodec = QTextCodec.codecForName("utf8")
app = QPEApplication.new([$0]+ARGV)
app.setDefaultCodec($defaultCodec)
QApplication.setFont(QFont.new("lcfont",18))
app.showMainWidget(SampleWindow.new)
app.exec