2016-04-10 7 views
0

私はQt/C++プログラマです。今日私は糸の安全性に取り組んでおり、これについて数多くの議論がありました。共有されている読み取り/書き込み変数の中に割り込みスレッド C/C++でオブジェクトを作成する際のコンパイラの最適化の境界は何ですか?

  • 信号の間で共有される変数のスレッド
  • コンパイラの最適化の間に/書き込み変数を読みながら

    1. 原子性:それから私は4つのタイトルで安全性の問題をリストアップしました私は二句を除くこれらのすべてを理解している

    スレッド間で共有される変数の同時読み取り/書き込みのスレッド間

  • 混乱。明白ではないので、理解するのは非常に複雑だと思います。コンパイラの動作に依存し、コンパイラの動作を正しく予測するにはどうすればよいですか。たとえば:

    int i=0; 
    void foo() 
    { 
        for (;i<100;i++) 
        {} 
    { 
    

    いくつかのリソースによると、上記のコードで、コンパイラが完成数えるまで、CPUのメモリからレジスタにiを移動する、その最終的な値でそれを書き戻します。したがって、上記のコードがカウントされている間に別のスレッドがiの値を読み取ろうとするとどうなりますか。カウントが終了するまでゼロになります。カウントが終了するまで、実際の価値は依然としてCPU上にあるからです。したがって予期しないステータスが発生します。それはmain()機能に初期化されているため、Qtのドキュメント、メインスレッドが所有threadオブジェクトのメンバ変数に応じて、上記のコードで

    class MyThread : public QThread 
    { 
        Q_OBJECT 
    
        void run() 
        { 
         mutex.lock(); 
         status=true; 
         mutex.unlock(); 
        } 
    
        private: 
         QMutex mutex; 
         bool status; 
    }; 
    
    int main() 
    { 
        MyThread thread; 
        thread.start(); 
        return 0; 
    } 
    

    、およびrun()関数のコードは、第二上で実行されていますのは、多くの他の例を見てみましょう糸。そして、私はmutexオブジェクトをアクセスとアトミック性のシリアライゼーションに使用しました。しかし、どのように私はそれを知ることができる、コンパイラは実際にmutexオブジェクトをメモリに初期化した後、2番目のスレッドはrun()関数でそれを使用しました。コンパイラはシーケンシャルコードフローで実際にmutexオブジェクトの使用を見たことがないので、メモリにロードできない可能性があります。例えば、コンパイル時に、コンパイラは、追加メモリを獲得するためにシーケンシャルコードフローで使用されないいくつかの変数を削除したり、いくつかのメモリオーダ操作後にすべてのメンバ値をメモリに書き込むことがあります。どうすればそれを知ることができますか?コンパイラが変数を最適化したかどうかをどのように知ることができます

  • +0

    すべてのスレッドには独自のスタックがあることに注意してください。各スレッドはあなたのforループを独立して実行します。 –

    +0

    この質問の編集は、最初の部分を完全に変更しました。時間を費やした人は、時間を無駄にしていました。これはあまり準備ができておらず、文句のつかない質問です。 –

    +0

    申し訳ありませんが、私はあなたの時間を無駄にしましたが、もっと説明したかったのです。 –

    答えて

    1

    しかし、どのように私は2番目のスレッドが実行()関数でそれを使用する前に、コンパイラが実際にメモリにミューテックス オブジェクトを初期化し、ことを知ることができます。

    第1の原則から独自のミューテックスクラスを設計していた場合、これは本当に心配する必要がある問題です。コンパイラは最適化プロセスの一環としてコードの順序を並べ替えることができるだけでなく、コンパイラがあなたのことを混乱させない場合でも、現代のCPUは命令を実行する順番を並べ替えることが多いため、可能な限りよく利用されるように、実行パイプラインをより良く保つためです。

    、それが結果として、プログラムの観察可能な行動はあなたが書いたソースコードの動作と区別がつかない限り、望んでいるあなたのコードに任意の狂気の変換を行うことができます言う++コンパイラが"as-if rule"下で動作Cに含まオプティマイザ。シングルスレッドのプログラムでは、あるスレッドが他のスレッドの変数を同期せずに読み書きしようとすると、コンパイラの(そしてCPUの)巧妙な最適化のすべてが2番目のスレッドに "可視"になり、したがって、2番目のスレッドは、同期に非常に注意しない限り、未定義の(読んでいると奇妙で予期しない)動作をする可能性があります。

    pthreadsライブラリ(QMutexとQThreadクラスが内部実装で使用している可能性がある)のような低レベルのスレッドライブラリの作成者は、この混乱をすべて隠していますか?彼らはmemory barriersoptimization barriersを適切な場所のコードに挿入することでそれを行います。最適化の障壁は、コンパイラに「最適化中にこの点を超えてアクセスを移動しないでください。また、メモリバリアは、実行時にCPUによって処理され、アウトオブオーダー実行の最適化が制約される以外は同様です。 QMutexやQThreadクラスのメンバ関数のコードは、必要に応じてすでにあなたのためにそれを行っているので、あなたはQMutexのような高レベルの構造体を使用しているので、あなた自身で障壁を指定することを心配する必要はありません。

    しかし、質問:デフォルトでは、コンパイラの最適化には、プログラマやC++仕様で明示的に指定されているもの以外に境界はありません(C++仕様は、できるだけリベラルであることを本当に難しくしています。オプティを排除するそれが必要ない場合は、mizations)。

    +0

    あなたの答えは完璧で、私が必要としていたものでした。実際には、私が見逃したことを見せてくれました。ありがとう、ありがとう。だから、私は完全に私はマルチスレッドアプリケーションをコーディングするコンパイラを信頼することはできません。一方、あなたが言ったように、私はより高いレベルのプロフェッショナル・ライブラリを信頼することができます。あなたが再び言ったように、彼らはすべてその定義において必要なことをしているからです。実際に彼らはそうしています.... –

    +0

    ...私はミスしている、私はQMutexコードを調べた後、私はmutex.unlock()関数でメモリバリアーメカニズムを見て、すべてのものがロック解除前に書いてあることを確認している()が完了しました。しかし、私はQMutex()コンストラクタ関数上で同じメカニズムを考えることはできませんが、今私はそれがコンストラクタ関数にメモリの障壁を持っている必要があり、それは非常に賢明だと思う。 –

    +0

    通常は、QMutexオブジェクトを作成する前にQMutexオブジェクトを作成します。それ以外の場合、コンストラクターが実行を終了する前に、他のスレッドがQMutexをロックしようとするリスクがあります。その場合、pthread_create()コールによって必要な障壁が提供されます。既に動作している別のスレッドが直ちに使用するQMutexを作成している(まれにしかない)特別なケースでは、競合状態を避けるために(おそらく2番目のQMutexを使用して)スレッドの動作をどうにか同期させる必要があります。 –

    関連する問題