(优化?)关于GCC std :: thread的Bug

iam*_*nka 14 multithreading clang c++11 gcc4.7

在测试某些功能的同时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();
}
Run Code Online (Sandbox Code Playgroud)

以下备用代码有效:

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;
}
Run Code Online (Sandbox Code Playgroud)

我们认为它可能与函数的arity的无序评估有关,或者它只是std::thread在复制非std::ref限定参数时应该工作的方式.然后我们用Clang测试了第一个代码并且它可以工作(因此开始怀疑GCC错误).

编译使用:GCC 4.7,Clang 3.2.1

编辑: GCC代码使用第一个版本的代码给出了错误的输出,但是对于第二个版本,它给出了正确的输出.

Rob*_*Hsu 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我添加了lambda function 的first和指针的打印,并发现了这个有趣的结果(当未定义时):lastfFIX

**Pointer:0x28abd8 | 0x28abe4
1 2 3 **Pointer:0x28abf0 | 0x28abf0
**Pointer:0x28abf0 | 0x28ac00
7 8 9 10
Run Code Online (Sandbox Code Playgroud)

#ELSE然后我为该案例添加一些代码#ifndef FIX。效果很好。

- 更新:这个结论,下面的原始帖子,是错误的。我的错。请参阅下面乔什的评论 -

我相信threads[]的第二行std::thread{f, (first = to), (to += increment)},包含一个错误:解析器可以以任何顺序评估两对括号内的赋值。然而,构造函数的第一个、第二个和第三个参数的赋值顺序需要保持给定的顺序。

--- 更新:已更正 ---

因此,上面的调试打印结果表明 GCC4.8.2(我的版本)仍然有错误(更不用说 GCC4.7),但 GCC 4.9.2 修复了这个错误,正如 Maxim Yegorushkin 所报告的(参见上面的评论)。