「BeginGetResponse」タグアーカイブ

.NET FrameworkのBeginGetResponseメソッドが実行中のスレッドをブロックしてしまう件


数日前の事ですが、当サイトで公開しているDMMアフィリエイトの商品リンク作成ツールの利用者の方から「今までに比べてリンクの作成に非常に時間がかかるようになった」とのご報告を頂きました。

今までは遅くても数秒以内に作成できていた物が、早くても10秒以上かかるというのです。

こちらではしばらくプログラムの変更は行っていませんでしたので、単にDMMのサーバーが重くなっているのではと考えていましたが実は完全に私のミスでした。

何が原因だったのか

当サイトのDMMアフィリエイトのリンク作成ツールでは、開発言語としてC#が用いられています。つまりは、.NET Frameworkの機能を利用して実装されているということです。

プログラム中では、ユーザーの問い合わせに応じてDMMのサーバーから実際に商品情報と商品パッケージ画像を取得するのですが、この時に.NET FrameworkのBeginGetResponseメソッドを用いています。実はこのメソッドにちょっとした問題がありました。

このメソッドはインターネットリソースの非同期要求を開始するメソッドであり、通常このメソッドが呼ばれた瞬間に、要求の完了・未完を問わずに一瞬で呼び出し元に制御が返ってきます。

ところが、条件によってはBeginGetResponseメソッドから制御が帰らずにスレッドが長時間ブロックされたままになってしまうことがあります。私のツールがリンクの作成に非常に時間がかかるようになったのもこれが原因でした。

どんな条件の時にスレッドがブロックされるのか

例によって、海外サイトを検索していたら次のサイトを発見しました。

webrequest.begingetresponse is taking too much time when the url is invalid
https://stackoverflow.com/questions/1232139/webrequest-begingetresponse-is-taking-too-much-time-when-the-url-is-invalid

要約するとインターネットからのリソースの取得は非同期的に行われるけどDNSの解決なんかは内部で同期的に実行されるから、無効なURLなんかを指定してDNSの解決に失敗したりするとスレッドがその間ブロックされてしまうとのこと。

ところが。
私のプログラムでは無効なURLは指定していませんし、そもそもDMMの商品URLに通常のブラウザからアクセスするとあっという間に結果が表示されます。

しかし、同時にとある事を思い出しました。

実は数日前にApacheの実行ユーザをWindowsサービスのデフォルトユーザから、セキュリティをガチガチに固めた「Apache」というユーザ変更しました。そして改めて検証してみると、Apacheをデフォルトユーザで起動した場合にはリンク作成ツールは今まで通りの速度で動作するのですが、ユーザを「Apache」に変更すると10秒以上の遅延が発生するのです。

ひょっとして、ユーザ「Apache」のインターネット接続設定がおかしいのでは?と思い、ユーザ「Apache」でログオンしていみたところ・・・。

容疑者発見!

internetoption.png

「インターネットオプション」を開いてみたところなぜか「設定を自動的に検出する」というチェックボックスがオンになっていました。物は試しにこのチェックボックスをオフにして再度リンク作成ツールをテストしてみると、見事に遅延は解消されました。犯人はおまえだったのか。

参考までに、なんでこのような現象が起きるのかを調べてみたのですが、マイクロソフトの英語版公式リファレンス によると、こんな記述がありました。

The BeginGetResponse method requires some synchronous setup tasks to complete (DNS resolution, proxy detection, and TCP socket connection, for example) before this method becomes asynchronous. As a result, this method should never be called on a user interface (UI) thread because it might take some time, typically several seconds. 

要約:このメソッドは非同期状態になる前に、いくつかの同期タスク(例えば、DNS解決、プロキシ検出、TCPソケット接続等)を完了する必要があり、場合によってはそれらの同期タスクには数秒を要するため、このメソッドはUIスレッドから呼び出されるべきではありません!

 なお、日本語版のリファレンスには載っていませんでした・・・。


まとめ

 というわけで、どうやらスレッドをブロックしている要因はDNS解
決だけではなく、プロキシ検出なども含まれるため、「設定を自動的に検出する」オプションにチェックが入っているにもかかわらず設定の検出に失敗したり時間を要した場合には、その間スレッドはブロックされてしまうっぽいです。


 ところで、デフォルトでは「設定を自動的に検出する」はオフになるはずなのですが、なぜオンになっていたのでしょうね。