Code

2015年5月31日日曜日

Task.Run と Autofac データベース Connection 問題から、サーバー側マルチスレッドの検討

 今開発しているシステムは HTTP を使って、RESTful API です。Front end は iOS から、Android、WebSite 全部サポートしています。デバイスの 3G 回線でも、できるだけ速くロードするために、いろいろ工夫しました。例えば、一つ目のレスポンスに各リソースをリクエストする URL を入れて、実際の作成は後回しして、マルチスレッドを使います。さらに、Task.Run を使って、できるだけ時間のかかる処理を別のスレッドで行うようにします。一方、Oracle DB Connection などは Autofac を使って、 DI の .InstancePerRequest() で各スレッドに配っています。そうすると、Task.Run は Thread Pool に新しいスレッドを申請してので、そのスレッドでは前の Request のスレッドのリソースが使えなくなりました。。。"Invalid operation on a closed object" みたいなエラーが発生したりしています。毎回ではないですが、かなりの確率で発生しています。
 解決の方法としては、Task.Run を使ってるスレッドの中に、自分で Autofac の LifetimeScope コントロールすることです。 まず、LifetimeScope の Provider を作って、RequestLifetimeScopeTag を ILifetimeScope の作成関数に設定することで、この ILifetimeScope は Request Scope として指定できます。ただ、自分で ILifetimeScope を作ったから、Scope の Dispose() 関数を自分で呼び出す必要になります。後日まだどうするかのコードを貼り付けます。
 ここでまず話したいことは Task.Run を使う場合、スレッドの切り替え処理もありますので、一般的に 50 ms 以下のタスクでは別スレッドを使う必要がないのです。
 Task.Run は Back End でファイルを書く場合や、HttpClient を使って、他のスステムに Http Request を発行する場合使ったほうがいいです。一般的な DB アクセスには速度がかなり早いので、20ms ぐらいと言われているので、必要がないと思われています。
 もし Loop を使って、DB 更新を行う場合や、複数のテーブルを更新するが更新結果は Front end に返すする必要がない場合、できるだけ小回りにして、複数回で更新したほうがいいです。この処理は Task.Run を使うより、Hangfire などの Background タスク Runner を使ったほうがいいです。Hangfire は DB を使っているので、Retry 処理もあるし、Task.Run より安全です。
 Front end から見ると、Http API を呼ぶとき、レスポンスが戻ってきたら、 Callback 関数が呼ばれるので、一回の Http Request より、複数の Request を発行して、各 Callback 関数を呼び出して、UI をアップデートしたほうが速いです。Http Request 自体がマルチスレッドでサーバー側で処理するので、それを最大限に使わないと。

 それでは。

2015年5月16日土曜日

iOS ユニバーサルデザイン実践:イメージのセンター揃い

 この Sprint では、チームの iOS Dev が休暇で一ヶ月ほど離れることになりました。あまり Native サイドのコードを触りたくないですが、急いで実現したい機能があったので、一週間ほど Objective-C を使って、 開発をしました。
 まだ iOS の開発では Nib をベースにしていますが、xib フィアルの代わりに StoryBoard が使われるようになりました。さらに、違うサイズのデバイスを対応するために、ユニバーサルデザインという概念が導入されました。これは前と違って、 絶対位置を指定するより、Constraint を使って、相対位置とサイズを指定することで、大きい iPhone 6 plus から、細長い iPhone 5 まで、全てのスクリーンサイズを対応できる layout nib になります。
 さらに、Segue という概念が導入されて、前のように ViewController を push するコードより、もっと具体化になるものです。Ctrl を押しながら、ドラッグして、次画面はどこに遷移するかを指定します。これも面白いです。
 今回の機能は幾つかのイメージをスクリーンの真ん中に表示することです。前なら、イメージビューをプログラムで作成して、addSubView を読んで、画面に追加します。そのあと、Frame を取得して、計算後、位置を指定します。少しは面倒くさいデスけど。
 ただ、ユニバーサルデザインでは、これは変なコードになるので、どうするかを少し調べたら、一番いい方法が見つかりました。
 具体的に、parent view を作って、centre align の Constraint を指定します。同時にこの Constraint をoutlet で view に追加します。Constraint の Constant などのパラメーターが取得できるようになります。
 次、イメージを parent に追加したら、parent view の width を取得して、先ほどの centre constraint に設定します。これで、どうなスクリーンサイズでも、parent view がセンターになります。
 
 Constraint は iOS が各ビューの位置や、サイズを決めるために使ったものなので、さらに面白い使い方がありますね。さらに、こちらの値もアニメーションで使えるので、前のように origin.y を使ったりすることなくても、 view を動かしたりすることもできます。
 それでは。