none
マルチスレッドでのDNS問い合わせ

    質問

  • 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関数を複数スレッドから呼び出す際には、関数呼び出しの排他制御をする必要はあるでしょうか?

    2018年3月29日 6:26

すべての返信

  • あくまで想像ですが、用途を考えると、DNSクエリなどの処理は複数のプロセスから呼び出されうるため、単にスレッドセーフである以上にそもそもプロセスセーフなはずです。返却されるリストオブジェクトもハンドルではなくポインタとして返ってくるので、共有メモリなどではなく、プロセス固有かつスレッド固有のローカルメモリブロックとなるはずです。

    アプリケーションが明示的に排他制御を考慮しなければならない共有リソースにアクセスするAPIは、通例LockFile()などのようなロック用のAPIが用意されています。

    • 編集済み sygh 2018年3月29日 15:12
    2018年3月29日 15:10
  • あくまで想像ですが、用途を考えると、DNSクエリなどの処理は複数のプロセスから呼び出されうるため、単にスレッドセーフである以上にそもそもプロセスセーフなはずです。返却されるリストオブジェクトもハンドルではなくポインタとして返ってくるので、共有メモリなどではなく、プロセス固有かつスレッド固有のローカルメモリブロックとなるはずです。

    アプリケーションが明示的に排他制御を考慮しなければならない共有リソースにアクセスするAPIは、通例LockFile()などのようなロック用のAPIが用意されています。


    おっしゃる通り、プロセスセーフであるという点については疑う余地がありません。

    しかし、概念としてプロセスセーフならばスレッドセーフとは言えないと思います。

    DnsQuery関数が内部でstatic変数の形でプロセス内にキャッシュを持つようなことをしていると、

    マルチスレッドでの呼び出しに耐えられないことになります。

    # gethostbynameがスレッドセーフでないのと同じような状況

    関数仕様においてキャッシュ機構が明らかになっていないため、スレッドセーフか否かの判断ができないという点で困っています。

    ですので、その点に関して決定的な情報があればお教え頂けると非常にありがたいです。

    2018年4月2日 10:36
  • gethostbyname()関数はスレッドセーフなはずです。内部ではTLS (Thread Local Storage) が使用されており、スレッド固有のメモリが割り当てられるとあります。また、メモリはスレッドが終了するときに解放されるとあります。

    DnsQuery()に関してはドキュメントに明記されていませんが、どうしても公式の情報が必要であればMSのサポートに問い合わせるべきだと思います。もし仮にスレッドセーフでないのであれば、逆に注意点としてその旨がドキュメントに明記されていると思いますが。

    なお、DnsQuery()関数の説明にて記載されているresolver cacheというのは、プロセスごとのキャッシュではなく、システム全体で共有するキャッシュです。

    Windowsの名前解決のトラブルシューティング(DNSリゾルバーキャッシュ編):Tech TIPS - @IT

    • 編集済み sygh 2018年4月2日 13:25
    2018年4月2日 13:10
  • Linuxのgethostbynameがスレッドセーフでないので、同じくスレッドセーフでないと誤解していました。

    ご指摘・情報ありがとうございます。


    2018年4月3日 3:43
  • スレッドセーフではない、という情報がありました http://www.geekpage.jp/programming/winsock/tips-gethostbyname.php
    いつ書かれたものかわかりませんが
    私がリンクした情報が間違っているようですね。Geekなページのコードはマルチスレッドではないですね。で、MSDNには、(スレッドごとに)1個のメモリを割り当てる、と書かれています。スレッドセーフだけれど、1回の問い合わせごとに退避させなければならない、ですね。失礼しました。

    Jitta@わんくま同盟



    • 編集済み Jitta 2018年4月3日 4:34 追加
    2018年4月3日 4:26
  • 時代がかわってしまっていて、今は 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()スレッドセーフかどうかしりませんが、スレッドセーフな関数を使っても
    スレッドセーフでない機能も作れちゃいますし。
    最終的にはアプリケーション実装者が責任を取るべきだとの主張のように読めました。
    まぁしかたないのかもしれません。

    2018年4月3日 5:39