Question
satoh_satoh on Thu, 29 Mar 2018 06:26:51
DNS問い合わせをするWindows APIにDnsQuery関数があります。
この関数で複数スレッドでのDNS問い合わせをしようと考えていますが、この関数はスレッドセーフでしょうか?
https://msdn.microsoft.com/ja-JP/library/windows/hardware/ms682016(v=vs.85).aspx
上記ページにDnsQuery関数の仕様が明記されておりますが、スレッドセーフであるか否かの記述がありません。
DNS問い合わせを行い、結果として渡されたデータ領域を明示的に開放しなくてはいけないことを考慮すると、
getaddrinfo関数のようにスレッドセーフであるようにも思われます。
パフォーマンスを考慮すると無用な排他制御は避けたいと思っています。
しかし上記ページにはOSのDNSキャッシュを参照するなど共有リソースにアクセスするように思われる記述もあり確証が持てません。
DnsQuery関数を複数スレッドから呼び出す際には、関数呼び出しの排他制御をする必要はあるでしょうか?
Replies
sygh on Thu, 29 Mar 2018 15:10:03
あくまで想像ですが、用途を考えると、DNSクエリなどの処理は複数のプロセスから呼び出されうるため、単にスレッドセーフである以上にそもそもプロセスセーフなはずです。返却されるリストオブジェクトもハンドルではなくポインタとして返ってくるので、共有メモリなどではなく、プロセス固有かつスレッド固有のローカルメモリブロックとなるはずです。
アプリケーションが明示的に排他制御を考慮しなければならない共有リソースにアクセスするAPIは、通例LockFile()などのようなロック用のAPIが用意されています。
satoh_satoh on Mon, 02 Apr 2018 10:36:43
あくまで想像ですが、用途を考えると、DNSクエリなどの処理は複数のプロセスから呼び出されうるため、単にスレッドセーフである以上にそもそもプロセスセーフなはずです。返却されるリストオブジェクトもハンドルではなくポインタとして返ってくるので、共有メモリなどではなく、プロセス固有かつスレッド固有のローカルメモリブロックとなるはずです。
アプリケーションが明示的に排他制御を考慮しなければならない共有リソースにアクセスするAPIは、通例LockFile()などのようなロック用のAPIが用意されています。
おっしゃる通り、プロセスセーフであるという点については疑う余地がありません。
しかし、概念としてプロセスセーフならばスレッドセーフとは言えないと思います。
DnsQuery関数が内部でstatic変数の形でプロセス内にキャッシュを持つようなことをしていると、マルチスレッドでの呼び出しに耐えられないことになります。
# gethostbynameがスレッドセーフでないのと同じような状況
関数仕様においてキャッシュ機構が明らかになっていないため、スレッドセーフか否かの判断ができないという点で困っています。
ですので、その点に関して決定的な情報があればお教え頂けると非常にありがたいです。
sygh on Mon, 02 Apr 2018 13:10:16
gethostbyname()関数はスレッドセーフなはずです。内部ではTLS (Thread Local Storage) が使用されており、スレッド固有のメモリが割り当てられるとあります。また、メモリはスレッドが終了するときに解放されるとあります。
DnsQuery()に関してはドキュメントに明記されていませんが、どうしても公式の情報が必要であればMSのサポートに問い合わせるべきだと思います。もし仮にスレッドセーフでないのであれば、逆に注意点としてその旨がドキュメントに明記されていると思いますが。
なお、DnsQuery()関数の説明にて記載されているresolver cacheというのは、プロセスごとのキャッシュではなく、システム全体で共有するキャッシュです。
satoh_satoh on Tue, 03 Apr 2018 03:43:02
Linuxのgethostbynameがスレッドセーフでないので、同じくスレッドセーフでないと誤解していました。
ご指摘・情報ありがとうございます。
Jitta on Tue, 03 Apr 2018 04:26:23
スレッドセーフではない、という情報がありました http://www.geekpage.jp/programming/winsock/tips-gethostbyname.php
いつ書かれたものかわかりませんが
私がリンクした情報が間違っているようですね。Geekなページのコードはマルチスレッドではないですね。で、MSDNには、(スレッドごとに)1個のメモリを割り当てる、と書かれています。スレッドセーフだけれど、1回の問い合わせごとに退避させなければならない、ですね。失礼しました。
Jitta@わんくま同盟
仲澤@失業者 on Tue, 03 Apr 2018 05:39:01
時代がかわってしまっていて、今は getaddrinfo()ですかね。
今やってみたら、gethostbyname()はVS2013ではコンパイルエラーになりました(level3)。
追及しても労多くしてなんとやらかもしれません。
本人の紹介ページもありますし。
http://www.geekpage.jp/programming/winsock/getaddrinfo.php
で、Winsock自体のスレッドセーフについては、
良く知られたページ「Winsock Programmer's FAQ」に、
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/intermediate.html#threadsafety
のような記述があります(なぜかIE11だと化けるのでChrome等他のブラウザで見てください)。
DnsQuery()スレッドセーフかどうかしりませんが、スレッドセーフな関数を使っても
スレッドセーフでない機能も作れちゃいますし。
最終的にはアプリケーション実装者が責任を取るべきだとの主張のように読めました。
まぁしかたないのかもしれません。