14

でいくつかの機能をテストしているstd::threadときに、友人が GCC で問題に遭遇しました。これが GCC のバグなのか、それともこのコードに何か問題があるのか​​ を尋ねる価値があると考えました (コードは (たとえば) "7 8 9 10 1 2 3" を出力します)。 、しかし、[1,10] のすべての整数が出力されることを期待しています):

#include <algorithm>
#include <iostream>
#include <iterator>
#include <thread>

int main() {
    int arr[10];
    std::iota(std::begin(arr), std::end(arr), 1);
    using itr_t = decltype(std::begin(arr));

    // the function that will display each element
    auto f = [] (itr_t first, itr_t last) {
        while (first != last) std::cout<<*(first++)<<' ';};

    // we have 3 threads so we need to figure out the ranges for each thread to show
    int increment = std::distance(std::begin(arr), std::end(arr)) / 3;
    auto first    = std::begin(arr);
    auto to       = first + increment;
    auto last     = std::end(arr);
    std::thread threads[3] = {
        std::thread{f, first, to},
        std::thread{f, (first = to), (to += increment)},
        std::thread{f, (first = to), last} // go to last here to account for odd array sizes
    };
    for (auto&& t : threads) t.join();
}

次の代替コードが機能します。

int main()
{
    std::array<int, 10> a;
    std::iota(a.begin(), a.end(), 1);
    using iter_t = std::array<int, 10>::iterator;
    auto dist = std::distance( a.begin(), a.end() )/3;
    auto first = a.begin(), to = first + dist, last = a.end();
    std::function<void(iter_t, iter_t)> f =
        []( iter_t first, iter_t last ) {
            while ( first != last ) { std::cout << *(first++) << ' '; }
        };
    std::thread threads[] {
            std::thread { f,  first, to },
            std::thread { f, to, to + dist },
            std::thread { f, to + dist, last }
    };
    std::for_each(
        std::begin(threads),std::end(threads),
        std::mem_fn(&std::thread::join));
    return 0;
}

関数のアリティの順序付けられていない評価と関係があるか、修飾されていない引数std::threadをコピーするときに機能するはずの方法であると考えました。std::ref次に、最初のコードを Clang でテストしたところ、動作しました (そのため、GCC のバグが疑われ始めました)。

使用コンパイラ: GCC 4.7、Clang 3.2.1

編集: GCC コードは、コードの最初のバージョンでは間違った出力を提供しますが、2 番目のバージョンでは正しい出力を提供します。

4

1 に答える 1

1

この変更されたプログラムから:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <thread>
#include <sstream>

int main()
{
    int arr[10];
    std::iota(std::begin(arr), std::end(arr), 1);
    using itr_t = decltype(std::begin(arr));

    // the function that will display each element
    auto f = [] (itr_t first, itr_t last) {
        std::stringstream ss;
        ss << "**Pointer:" << first  << " | " << last << std::endl;
        std::cout << ss.str();
        while (first != last) std::cout<<*(first++)<<' ';};

    // we have 3 threads so we need to figure out the ranges for each thread to show
    int increment = std::distance(std::begin(arr), std::end(arr)) / 3;
    auto first    = std::begin(arr);
    auto to       = first + increment;
    auto last     = std::end(arr);
    std::thread threads[3] = {
        std::thread{f, first, to},
#ifndef FIX
        std::thread{f, (first = to), (to += increment)},
        std::thread{f, (first = to), last} // go to last here to account for odd array sizes
#else
        std::thread{f,  to,  to+increment},
        std::thread{f,  to+increment, last} // go to last here to account for odd array sizes
#endif
    };
    for (auto&& t : threads) {
        t.join();
    }
}

とlambda function のポインターの出力を追加するfirstと、次のような興味深い結果が得られます (が未定義の場合):lastfFIX

**Pointer:0x28abd8 | 0x28abe4
1 2 3 **Pointer:0x28abf0 | 0x28abf0
**Pointer:0x28abf0 | 0x28ac00
7 8 9 10

#ELSE次に、ケースのコードをいくつか追加し#ifndef FIXます。それはうまくいきます。

- 更新: この結論、以下の元の投稿は間違っています。私のせいです。以下のジョシュのコメントを参照してください-

私は、std::thread{f, (first = to), (to += increment)},threads[] の 2 行目にバグが含まれていると考えています。2 対の括弧内の代入は、パーサーによって任意の順序で評価できます。ただし、コンストラクターの 1 番目、2 番目、3 番目の引数の割り当て順序は、与えられた順序を維持する必要があります。

--- 更新: 訂正 ---

したがって、上記のデバッグ出力の結果は、GCC4.8.2 (私のバージョン) にはまだバグがある (GCC4.7 とは言わない) ことを示唆していますが、Maxim Yegorushkin によって報告されているように、GCC 4.9.2 はこのバグを修正しています (上記のコメントを参照)。

于 2014-11-10T10:18:05.520 に答える