线程不能连接FORY每个并行C++

线程不能连接FORY每个并行C++,c++,multithreading,templates,C++,Multithreading,Templates,我编写了一个示例代码来运行for_的并行实例 在下面的代码中,我无法加入线程。我现在开始并发编程还不早,所以我不确定我是否把所有事情都做好了 template <typename Iterator, typename F> class for_each_block { public : void operator()(Iterator start, Iterator end, F f) { cout << this_thread::

我编写了一个示例代码来运行for_的并行实例 在下面的代码中,我无法加入线程。我现在开始并发编程还不早,所以我不确定我是否把所有事情都做好了

template <typename Iterator, typename F>
class for_each_block
{
public :
        void operator()(Iterator start, Iterator end, F f) {
            cout << this_thread::get_id << endl;
            this_thread::sleep_for(chrono::seconds(5));
            for_each(start, end, [&](auto& x) { f(x); });
    }
};

typedef unsigned const long int ucli;

template <typename Iterator, typename F>
void for_each_par(Iterator first, Iterator last, F f)
{
    ucli size = distance(first, last);
    if (!size)
        return;
    ucli min_per_thread = 4;
    ucli max_threads = (size + min_per_thread - 1) / min_per_thread;
    ucli hardware_threads = thread::hardware_concurrency();

    ucli no_of_threads = min(max_threads, hardware_threads != 0 ? hardware_threads : 4);

    ucli block_size = size / no_of_threads;

    vector<thread> vf(no_of_threads);
    Iterator block_start = first;
    for (int i = 0; i < (no_of_threads - 1); i++)
    {
        Iterator end = first;
        advance(end, block_size);
        vf.push_back(std::move(thread(for_each_block<Iterator, F>(),first,end,f)));
        first = end;
    }
    vf.push_back(std::move(thread(for_each_block<Iterator, F>(), first, last, f)));
    cout << endl;
    cout << vf.size() << endl;
    for(auto& x: vf)
    {
        if (x.joinable())
            x.join();
        else
            cout << "threads not joinable " << endl;
    }

    this_thread::sleep_for(chrono::seconds(100));
}

int main()
{
    vector<int> v1 = { 1,8,12,5,4,9,20,30,40,50,10,21,34,33 };
    for_each_par(v1.begin(), v1.end(), print_type<int>);
return 0;
}
在上面的代码中,我得到了不可连接的线程。我也尝试过异步期货,但我还是得到了同样的结果。我是不是遗漏了什么

非常感谢您的帮助, 先谢谢你

vector<thread> vf(no_of_threads);
注意:std::临时移动是多余的:;考虑改变这一点:

vf.push_back(std::move(thread(for_each_block<Iterator, F>(), first, last, f)));
为此:

vf.emplace_back(for_each_block<Iterator, F>(), first, last, f);
注意:std::临时移动是多余的:;考虑改变这一点:

vf.push_back(std::move(thread(for_each_block<Iterator, F>(), first, last, f)));
为此:

vf.emplace_back(for_each_block<Iterator, F>(), first, last, f);

这可能有趣,也可能不有趣。我尝试过重构代码,以使用我认为更惯用的方法。我并不是说你的方法是错误的,但既然你正在学习线程管理,我想你可能会对其他可能的方法感兴趣

如有必要,请随意提问。内联评论:

#include <vector>
#include <chrono>
#include <thread>
#include <mutex>
#include <iomanip>
#include <future>

using namespace std;

//
// provide a means of serialising writing to a stream.
//
struct locker
{
    locker() : _lock(mutex()) {}

    static std::mutex& mutex() { static std::mutex m; return m; }
    std::unique_lock<std::mutex> _lock;
};
std::ostream& operator<<(std::ostream& os, const locker& l) {
    return os;
}

//
// fill in the missing work function
//
template<class T>
void print_type(const T& t) {
    std::cout << locker() << hex << std::this_thread::get_id() << " : " << dec << t << std::endl;
}

// put this in your personable library.
// the standards committee really should have given us ranges by now...
template<class I1, class I2>
struct range_impl
{
    range_impl(I1 i1, I2 i2) : _begin(i1), _end(i2) {};

    auto begin() const { return _begin; }
    auto end() const { return _end; }

    I1 _begin;
    I2 _end;
};

// distinct types because sometimes dissimilar iterators are comparable
template<class I1, class I2>
auto range(I1 i1, I2 i2) {
    return range_impl<I1, I2>(i1, i2);
}

//
// lets make a helper function so we can auto-deduce template args
//
template<class Iterator, typename F>
auto make_for_each_block(Iterator start, Iterator end, F&& f)
{
    // a lambda gives all the advantages of a function object with none
    // of the boilerplate.
    return [start, end, f = std::move(f)] {
        cout << locker() << this_thread::get_id() << endl;
        this_thread::sleep_for(chrono::seconds(1));

        // let's keep loops simple. for_each is a bit old-skool.
        for (auto& x : range(start, end)) {
            f(x);
        }
    };
}


template <typename Iterator, typename F>
void for_each_par(Iterator first, Iterator last, F f)
{
    if(auto size = distance(first, last))
    {
        std::size_t min_per_thread = 4;
        std::size_t max_threads = (size + min_per_thread - 1) / min_per_thread;
        std::size_t hardware_threads = thread::hardware_concurrency();

        auto no_of_threads = min(max_threads, hardware_threads != 0 ? hardware_threads : 4);

        auto block_size = size / no_of_threads;

        // futures give us two benefits:
        // 1. they automatically transmit exceptions
        // 2. no need for if(joinable) join. get is sufficient
        //
        vector<future<void>> vf;
        vf.reserve(no_of_threads - 1);
        for (auto count = no_of_threads ; --count ; )
        {
            //
            // I was thinking of refactoring this into std::generate_n but actually
            // it was less readable.
            //
            auto end = std::next(first, block_size);
            vf.push_back(async(launch::async, make_for_each_block(first, end, f)));
            first = end;
        }
        cout << locker() << endl << "threads: " << vf.size()  << " (+ main thread)" << endl;

        //
        // why spawn a thread for the remaining block? we may as well use this thread
        //
        /* auto partial_sum = */ make_for_each_block(first, last, f)();

        // join the threads
        // note that if the blocks returned a partial aggregate, we could combine them
        // here by using the values in the futures.
        for (auto& f : vf) f.get();
    }
}

int main()
{
    vector<int> v1 = { 1,8,12,5,4,9,20,30,40,50,10,21,34,33 };
    for_each_par(v1.begin(), v1.end(), print_type<int>);
    return 0;
}
请在此处解释std::move:[开始,结束,f=std::movef]{…}

这是c++14中提供的一个受欢迎的语言特性。捕获块内的f=std::movef等同于:decltypef new\u f=std::movef,只是新变量称为f而不是new\u f。它允许我们将对象移动到lambda中,而不是复制它们


对于大多数函数对象来说,这无关紧要,但有些函数对象可能很大,这使编译器有机会使用move而不是copy(如果可用)。

这可能有趣,也可能不有趣。我尝试过重构代码,以使用我认为更惯用的方法。我并不是说你的方法是错误的,但既然你正在学习线程管理,我想你可能会对其他可能的方法感兴趣

如有必要,请随意提问。内联评论:

#include <vector>
#include <chrono>
#include <thread>
#include <mutex>
#include <iomanip>
#include <future>

using namespace std;

//
// provide a means of serialising writing to a stream.
//
struct locker
{
    locker() : _lock(mutex()) {}

    static std::mutex& mutex() { static std::mutex m; return m; }
    std::unique_lock<std::mutex> _lock;
};
std::ostream& operator<<(std::ostream& os, const locker& l) {
    return os;
}

//
// fill in the missing work function
//
template<class T>
void print_type(const T& t) {
    std::cout << locker() << hex << std::this_thread::get_id() << " : " << dec << t << std::endl;
}

// put this in your personable library.
// the standards committee really should have given us ranges by now...
template<class I1, class I2>
struct range_impl
{
    range_impl(I1 i1, I2 i2) : _begin(i1), _end(i2) {};

    auto begin() const { return _begin; }
    auto end() const { return _end; }

    I1 _begin;
    I2 _end;
};

// distinct types because sometimes dissimilar iterators are comparable
template<class I1, class I2>
auto range(I1 i1, I2 i2) {
    return range_impl<I1, I2>(i1, i2);
}

//
// lets make a helper function so we can auto-deduce template args
//
template<class Iterator, typename F>
auto make_for_each_block(Iterator start, Iterator end, F&& f)
{
    // a lambda gives all the advantages of a function object with none
    // of the boilerplate.
    return [start, end, f = std::move(f)] {
        cout << locker() << this_thread::get_id() << endl;
        this_thread::sleep_for(chrono::seconds(1));

        // let's keep loops simple. for_each is a bit old-skool.
        for (auto& x : range(start, end)) {
            f(x);
        }
    };
}


template <typename Iterator, typename F>
void for_each_par(Iterator first, Iterator last, F f)
{
    if(auto size = distance(first, last))
    {
        std::size_t min_per_thread = 4;
        std::size_t max_threads = (size + min_per_thread - 1) / min_per_thread;
        std::size_t hardware_threads = thread::hardware_concurrency();

        auto no_of_threads = min(max_threads, hardware_threads != 0 ? hardware_threads : 4);

        auto block_size = size / no_of_threads;

        // futures give us two benefits:
        // 1. they automatically transmit exceptions
        // 2. no need for if(joinable) join. get is sufficient
        //
        vector<future<void>> vf;
        vf.reserve(no_of_threads - 1);
        for (auto count = no_of_threads ; --count ; )
        {
            //
            // I was thinking of refactoring this into std::generate_n but actually
            // it was less readable.
            //
            auto end = std::next(first, block_size);
            vf.push_back(async(launch::async, make_for_each_block(first, end, f)));
            first = end;
        }
        cout << locker() << endl << "threads: " << vf.size()  << " (+ main thread)" << endl;

        //
        // why spawn a thread for the remaining block? we may as well use this thread
        //
        /* auto partial_sum = */ make_for_each_block(first, last, f)();

        // join the threads
        // note that if the blocks returned a partial aggregate, we could combine them
        // here by using the values in the futures.
        for (auto& f : vf) f.get();
    }
}

int main()
{
    vector<int> v1 = { 1,8,12,5,4,9,20,30,40,50,10,21,34,33 };
    for_each_par(v1.begin(), v1.end(), print_type<int>);
    return 0;
}
请在此处解释std::move:[开始,结束,f=std::movef]{…}

这是c++14中提供的一个受欢迎的语言特性。捕获块内的f=std::movef等同于:decltypef new\u f=std::movef,只是新变量称为f而不是new\u f。它允许我们将对象移动到lambda中,而不是复制它们


对于大多数函数对象来说,这无关紧要,但有些函数对象可能很大,这使编译器有机会使用移动而不是副本(如果可用)。

非常感谢,它现在可以工作了。但我有一个问题。所有的线程打印相同的id。你能不能也请建议更多我相信我可能做了一些错误的方式。另外,我总是对push_back和emplace_back感到困惑,感谢您的澄清:@KartikV this_thread::get_id是一个函数,应该调用它。现在你正在打印函数指针值。@Ocelot太真实了,我觉得自己太愚蠢了。你们两个都睁开了我的眼睛。我应该仔细观察,避免犯愚蠢的错误。谢谢你们,非常感谢,现在可以用了。但我有一个问题。所有的线程打印相同的id。你能不能也请建议更多我相信我可能做了一些错误的方式。另外,我总是对push_back和emplace_back感到困惑,感谢您的澄清:@KartikV this_thread::get_id是一个函数,应该调用它。现在你正在打印函数指针值。@Ocelot太真实了,我觉得自己太愚蠢了。你们两个都睁开了我的眼睛。我应该仔细观察,避免犯愚蠢的错误。谢谢你们。它很整洁,储物柜,靶场都很酷。你能解释一下你在函数参数中使用的移动语义吗?非常感谢。你的助手功能非常有用,你能给我指点更多的博客吗?或者如果你有时间C++,你可以写一个。其中大部分似乎都是不太懂英语的人写的。告诉我霍奇斯的事。r@gmail.com有问题,或者你可以看看我的其他答案。在堆栈溢出方面有一些严肃的C++专家。如果我遇到什么事,我会告诉你的。我一直在考虑当我的下一项工作有望在一个月内完成时,我会开办一些课程。它非常整洁,柜子,靶场都很酷。你能解释一下你在函数参数中使用的移动语义吗?非常感谢。你的助手功能非常有用,你能给我指点更多的博客吗?或者如果你有时间C++,你可以写一个。其中大部分似乎都是不太懂英语的人写的。告诉我霍奇斯的事。r@gmail.com有问题,或者你可以看看我的其他答案。在堆栈溢出方面有一些严肃的C++专家。如果我遇到什么事,我会告诉你的。我一直在考虑在我的下一项工作有望在一个月内完成时开办一些课程。