【Rails】Controllerで重い処理を実行する
(Controllerに限らないですが)重い処理があると、htmlレンダリングはその処理後に行われるためユーザにはフリーズしたように見えてしまいます。(´・ω・`)
〜コントローラで〜
def heavyTask #重い処理 sleep(1000) end
、、、1000秒待たないと画面が表示されないよ(´・ω・`)
重い処理はThread.start()で別スレッド化して、同時にhtmlレンダリング出来るようにしましょう。
def heavyTask Thread.start do #重い処理 sleep(1000) end #重い処理を待たずにhtmlレンダリングできるよ(`・ω・´) end
以上のコードで、重い処理と画面表示を並列化出来ます。ぱちぱち。
ん、重い処理が終わったらその通知を画面に表示したいのですか?
それはまた別のおはなし。(←ホントは知らな(ry
【注意】
Threadを使う場合は、同時に同じ処理をしても平気か検討が必要です。
ウェブアプリだと何十、何百も同時リクエストがありますよね。
上記コードだと、リクエストごとにスレッドが立ち上がり、同時に動作します。
この際に、想定外のデータになってしまわないかよく検討しましょう。
- 想定外のデータにならない場合はおkです。(「スレッドセーフ」と言います)
- 想定外のデータになってしまう場合は「排他制御」を行わなくてはなりません。
排他制御はミューテックスという機能で実現出来ます。
平たく言うと、スレッド間で
「今わたしが処理してるから邪魔しないでね(・A・)」
「じゃあ待ってるから終わったら教えてね(´・ω・`)」
というのを実現する仕組みです。
def heavyTask #スレッド間で同じミューテックスを使うため、グローバル変数($)を使う if($mycontrollerMutex==nil) $mycontrollerMutex = Mutex.new end tid = Thread.start do p "Thread start. ID:#{tid}" $mycontrollerMutex.synchronize do #重い処理 end p "Thread end. ID:#{tid}" end
以上のようにすれば、heavyTask()内の重い処理は、リクエスト順に順番に処理されるようになります。
、、ちなみにモデル層でこれを実現するには
ActiveRecord::Base.allow_concurrency = true
とすればいいみたい。