none
Visual Studio2010とVisual Studio11のコンパイラによる違い RRS feed

  • 質問

  • 以下のコードをVisual Studio 2010とVisual Studio 11で実行すると、Visual Studio11でのみ例外が発生します。

    コードに問題があると思います(std::wcin.get())が、なぜVisual Studio 2010では問題がないのでしょうか?

    #include  <windows.h>
    #include  <process.h>
    #include  <iostream>
    
    class Sync {
      SRWLOCK & srw_lock_;
    
    public:
      Sync( SRWLOCK & srw_lock )
        : srw_lock_( srw_lock )
      {
        ::AcquireSRWLockExclusive( & srw_lock_ );
      }
      ~Sync()
      {
        ::ReleaseSRWLockExclusive( & srw_lock_ );
      }
    };
    
    SRWLOCK g_srw_lock;
    int     g_number = 0;
    unsigned WINAPI thread1( void * )
    {
      for( int i = 0; i != 10000; ++i ) {
        ::Sleep( 5 );
        Sync sync( g_srw_lock );
        std::wcout << L"thread1 : " << g_number << std::endl;
        g_number += 1;
      }
      return 0u;
    }
    
    unsigned WINAPI thread2( void * )
    {
      for( int i = 0; i != 10000; ++i ) {
        ::Sleep( 5 );
        Sync sync( g_srw_lock );
        std::wcout << L"thread2 : " << g_number << std::endl;
        g_number += 2;
      }
      return 0u;
    }
    
    int wmain()
    {
      ::InitializeSRWLock( & g_srw_lock );
    
      ::CloseHandle( reinterpret_cast< HANDLE >( ::_beginthreadex( nullptr, 0,
                        & thread1, nullptr,
                        0, nullptr ) ) );
      ::CloseHandle( reinterpret_cast< HANDLE >( ::_beginthreadex( nullptr, 0,
                        & thread2, nullptr,
                        0, nullptr ) ) );
    
      std::wcin.get();  // Visual Studio 11だと例外発生。2010では発生しない。
      //::Sleep( INFINITE );  // Visual Studio 11、2010共に例外発生しない
    
      return 0;
    }
    
    

     

     

    2011年11月29日 9:33

回答

  • std::wcin.get(); を std::cin.get(); に変更するとエラーが発生しなくなりますね。iostreamに対する変更がなんらかの問題を引き起こしているんでしょうね。
    • 回答としてマーク アイル 2011年12月3日 15:02
    2011年11月30日 3:52
  • 3連投になっちゃいますが、チグハグな返答しちゃってたみたいなのでご容赦ください。

     

    サンプルを少しいじってみてたら std::wcin.get() のみのコードで例外が発生するのに気付きました。

    どうも wcin 自体が現状の VS11 でうまく動かない感じです。

     

    発生箇所からいくとストリームのテキストモード取得に失敗してる?と思われますがどうしてですかね?

     

    「なぜVisual Studio 2010では問題がないか?」-> きちんと動作している。

    「なぜ例外が発生する?」-> 新規ランタイム更新によるバグ

    というのが自分の結論です。

     

    意外と Connect に挙がっている問題だったりするかもしれませんね繋がり悪くて調べてませんが。

     

    • 回答としてマーク アイル 2011年12月3日 15:03
    2011年12月3日 13:54

すべての返信

  • std::wcin.get(); を std::cin.get(); に変更するとエラーが発生しなくなりますね。iostreamに対する変更がなんらかの問題を引き起こしているんでしょうね。
    • 回答としてマーク アイル 2011年12月3日 15:02
    2011年11月30日 3:52
  • コードに不具合がありえるか等はまったく検証してませんが、

    例外発生箇所を見る限りストリーム読込で非ロック読取のため例外発生してるような感じですね。

     

    デバッガで停止しつつ動かすと例外発生せずに動くから、スレッド内のロック処理順序がなにか影響してそうな気配はありますがどうでしょう?

     

    2010で問題発生しないのならスレッド起動ルーチンにデバッガ動作と関連して何か修正が入ったのではないでしょうかね?

     

    参考になれば幸いですが・・・。

     

    • 回答としてマーク アイル 2011年12月3日 15:02
    • 回答としてマークされていない アイル 2011年12月3日 15:02
    2011年12月2日 22:55
  • 投稿してからソースを眺めると、参考になるかも知れないことに気付いたので再投稿します。

     

    ソースみるとメインスレッドでコンソール get() をして、各スレッドでコンソールに out() してますね。

    wchar 用のストリーム get() だからストリームのロック処理間に、スレッド内の out() が動いて異常になってるような気もします。

     

    佐祐理さんが言われているように wcin の iostream 処理が変更になったためのバグかも知れませんね。

    2011年12月2日 23:12
  • 3連投になっちゃいますが、チグハグな返答しちゃってたみたいなのでご容赦ください。

     

    サンプルを少しいじってみてたら std::wcin.get() のみのコードで例外が発生するのに気付きました。

    どうも wcin 自体が現状の VS11 でうまく動かない感じです。

     

    発生箇所からいくとストリームのテキストモード取得に失敗してる?と思われますがどうしてですかね?

     

    「なぜVisual Studio 2010では問題がないか?」-> きちんと動作している。

    「なぜ例外が発生する?」-> 新規ランタイム更新によるバグ

    というのが自分の結論です。

     

    意外と Connect に挙がっている問題だったりするかもしれませんね繋がり悪くて調べてませんが。

     

    • 回答としてマーク アイル 2011年12月3日 15:03
    2011年12月3日 13:54
  • 佐祐理さん
    kyano30さん

    ご回答ありがとうございます。

    C++の仕様でこの処理(マルチスレッドでのwcout,wcin)が保証(未定義でない)しているか分からなかったこともあり質問いたしました。

    お二人の回答から見ますと、この処理自体は保証されているのかと思います。

    そのため、お二人を回答とさせていただきました。

    ありがとうございます。

     

    2011年12月3日 15:03
  • マルチスレッドで使えるかどうか?ということだったのならば、

    VC++固有でしょうけど問題ないという情報をみつけましたので挙げておきますね。

    Thread Safety in the Standard C++ Library

     

    2011年12月3日 15:16
  • リンク先がVS2003のものになっています。

    VS2010およびVS11の各ページには次のように書かれていて、一応スレッドセーフと解釈できます。(というか内部実装がどうであれ、wcin / wcoutという別オブジェクトなんだし。)

    It is safe to write to an object from multiple threads.
    Note: Reading from a stream buffer is not considered to be a read operation. It should be considered as a write operation, because this changes the state of the class.
    2011年12月4日 1:23
  • 佐祐理さん御指摘ありがとうございます。

     

    記述がかなり変わってるとは思わなかったです。質問対象の版資料のがよかったですね。

     

    しかし、 別オブジェクトとはいっても操作対象は同一である可能性があるので仕様とか内部実装次第になるのでは?

    同一ファイルに対する入力ストリームと出力ストリームは別オブジェクトを作れますよね?

     

    wcin と wcout はコンソールの入出力なので入力と出力で分かれるかはOS次第な気がします。

     

    ※コンソールアプリなら標準入出力はコンソール入出力にと記述するべきですね。Winアプリだと違いそうですし・・。
    • 編集済み kyano30 2011年12月4日 7:01 追加訂正
    2011年12月4日 6:55
  • しかし、 別オブジェクトとはいっても操作対象は同一である可能性があるので仕様とか内部実装次第になるのでは?

    ひとこと足りませんでした。cin/wcin/cout/wcout間に依存関係がありそうなことは容易に想像できるとはいえ内部実装次第であり、

    ライブラリドキュメントとして、オブジェクト間にスレッドセーフかどうかについて言及すべき、もし明示的に言及されていないのであれば、他のオブジェクトと同じと捉え、オブジェクト間に依存関係なくスレッドセーフであると解釈されるべき

    と言いたかっただけです。

    # ま、VS2011で正常に動作するんですからVS11のバグでしょう、きっと。

    2011年12月4日 13:06