【问题标题】:deque::push_back with threads reacts strangely带线程的 deque::push_back 反应奇怪
【发布时间】:2017-05-12 21:03:00
【问题描述】:

我尝试了不同的方法,但是双端队列容器中线程的 push_back 反应很奇怪。

为什么会这样??这是因为复制/移动构造函数吗?

这里是程序的输出...

progress[0]:-1
progress[1]:-1
executing threads...
progress[0]:100
progress[1]:100
================================
progress[0]:-1
progress[1]:-1
executing threads...
progress[0]:-1
progress[1]:100

如输出所示,结果在使用之间有所不同

for(size_t i = 0; i < 2; ++i) {
    deq.push_back(th(&progress[i]));
}

deq.push_back(th(&progress[0]));
deq.push_back(th(&progress[1]));

这是源代码...

class th {
    public:
        void func() {
            *this->progress = 100;
        }

        th(int* prog) :
            progress(prog), 
            m_thread(std::thread(&th::func, this)) {};

        // COPY
        th(th const& other);
        th& operator=(th const& other);

        // MOVE
        th(th&&) = default;
        // th& operator=(th&& other) {
        //  if(this != &other){
        //  }
        //  return *this;
        // }

        void join() { m_thread.join(); }
        int *progress;

    private:
        std::thread m_thread;
};

int main(void) {

    {
        std::vector<int> progress;
        progress.push_back(-1);
        progress.push_back(-1);
        std::deque<th> deq;

        std::cout << "progress[0]:" << progress[0] << std::endl;
        std::cout << "progress[1]:" << progress[1] << std::endl;

        std::cout << "executing threads..." << std::endl;

        deq.push_back(th(&progress[0]));
        deq.push_back(th(&progress[1]));


        for (std::deque<th>::iterator it = deq.begin(); it != deq.end(); it++) {
            it->join();
            // deq.erase(it);
        }


        std::cout << "progress[0]:" << progress[0] << std::endl;
        std::cout << "progress[1]:" << progress[1] << std::endl;    
    }

    std::cout << "================================" << std::endl;

    {
        std::vector<int> progress;
        progress.push_back(-1);
        progress.push_back(-1);
        std::deque<th> deq;

        std::cout << "progress[0]:" << progress[0] << std::endl;
        std::cout << "progress[1]:" << progress[1] << std::endl;

        std::cout << "executing threads..." << std::endl;

        for(size_t i = 0; i < 2; ++i) {
            deq.push_back(th(&progress[i]));
        }

        for (std::deque<th>::iterator it = deq.begin(); it != deq.end(); it++) {
            it->join();
            // deq.erase(it);
        }

        std::cout << "progress[0]:" << progress[0] << std::endl;
        std::cout << "progress[1]:" << progress[1] << std::endl;
    }

    exit(EXIT_SUCCESS);
}

以及如何在没有编译器抱怨的情况下使用 deque 的擦除成员函数

    /home/user/test/obj/main.o: In function `__gnu_cxx::_Mutable_BidirectionalIteratorConcept<th*>::__constraints()':
main.cpp:(.text._ZN9__gnu_cxx37_Mutable_BidirectionalIteratorConceptIP2thE13__constraintsEv[__gnu_cxx::_Mutable_BidirectionalIteratorConcept<th*>::__constraints()]+0x40): undefined reference to `th::operator=(th const&)'
/home/user/test/obj/main.o: In function `__gnu_cxx::_ConvertibleConcept<th, th>::__constraints()':
main.cpp:(.text._ZN9__gnu_cxx19_ConvertibleConceptI2thS1_E13__constraintsEv[__gnu_cxx::_ConvertibleConcept<th, th>::__constraints()]+0x20): undefined reference to `th::th(th const&)'
/home/user/test/obj/main.o: In function `__gnu_cxx::_OutputIteratorConcept<th*, th>::__constraints()':
main.cpp:(.text._ZN9__gnu_cxx22_OutputIteratorConceptIP2thS1_E13__constraintsEv[__gnu_cxx::_OutputIteratorConcept<th*, th>::__constraints()]+0x64): undefined reference to `th::operator=(th const&)'
/home/user/test/obj/main.o: In function `th* std::__copy_move_backward<true, false, std::random_access_iterator_tag>::__copy_move_b<th*, th*>(th*, th*, th*)':
main.cpp:(.text._ZNSt20__copy_move_backwardILb1ELb0ESt26random_access_iterator_tagE13__copy_move_bIP2thS4_EET0_T_S6_S5_[th* std::__copy_move_backward<true, false, std::random_access_iterator_tag>::__copy_move_b<th*, th*>(th*, th*, th*)]+0x5c): undefined reference to `th::operator=(th const&)'
/home/user/test/obj/main.o: In function `th* std::__copy_move<true, false, std::random_access_iterator_tag>::__copy_m<th*, th*>(th*, th*, th*)':
main.cpp:(.text._ZNSt11__copy_moveILb1ELb0ESt26random_access_iterator_tagE8__copy_mIP2thS4_EET0_T_S6_S5_[th* std::__copy_move<true, false, std::random_access_iterator_tag>::__copy_m<th*, th*>(th*, th*, th*)]+0x44): undefined reference to `th::operator=(th const&)'
/home/user/test/obj/main.o: In function `__gnu_cxx::_Mutable_ForwardIteratorConcept<th*>::__constraints()':
main.cpp:(.text._ZN9__gnu_cxx31_Mutable_ForwardIteratorConceptIP2thE13__constraintsEv[__gnu_cxx::_Mutable_ForwardIteratorConcept<th*>::__constraints()]+0x3c): undefined reference to `th::operator=(th const&)'
collect2: ld returned 1 exit status
make: *** [link] Error 1

谢谢!

【问题讨论】:

  • // deq.erase(it); 是怎么回事?哦,是的,你的th 是错误的。它不应该是可复制的,并且应该正确实现移动语义 (noexcept)。
  • 如上所述编译器在使用它时会抱怨!
  • @mmmint 你有竞争条件,std::deque 不是线程安全容器。
  • 您将指针this 传递给std::thread 的构造函数。将th 对象移动到deque 后,this 的值不再有效,因为该对象现在已移动到不同的位置。然而线程仍然使用旧值。尝试使th 不可移动并使用emplace_back 而不是push_back
  • 出于同样的原因,如果从容器中删除其他 th 对象(以填充孔),则必须移动 th 对象,因此您将无法erase 来自deque 的对象与您的设计。

标签: c++ multithreading deque


【解决方案1】:

您将this 的值(即对象的当前地址)传递给std::thread 的构造函数。将th 对象移动到双端队列后,this 的值不再有效,因为该对象现在已移动到不同的位置。然而线程仍然使用旧值。尝试使th 不可移动并使用emplace_back 而不是push_back

class th {
    public:
        void func() {
            *this->progress = 100;
        }

        explicit th(int* prog) :
            progress(prog), 
            m_thread(std::thread(&th::func, this)) {};

        // COPY
        th(th const& other) = delete;
        th(th && other) = delete;
        th& operator=(th const& other) = delete;
        th& operator=(th &&) = delete;

        void join() { m_thread.join(); }
        int *progress;

    private:
        std::thread m_thread;
};

std::vector<int> progress;
progress.push_back(-1);
progress.push_back(-1);
std::deque<th> deq;
deq.emplace_back (&progress[0]);
// etc.

【讨论】:

  • 有没有其他容器可以用来在加入线程后删除元素?
  • 可能是某种链表。然而,我更喜欢将线程所需的数据存储在堆上的某个地方,而不是移动它。例如。将progress 设为unique_ptr&lt;int&gt; 并将progress.get () 的结果传递给std::thread 而不是this 的构造函数。当然,那么func 必须是静态的,并且接受int * progress 作为参数。
  • 或将deq 设为std::deque&lt;std::unique_ptr&lt;th&gt;&gt;。然后你有一组指向th 的指针,并且th 对象不需要移动。
猜你喜欢
  • 2016-04-03
  • 1970-01-01
  • 2011-05-12
  • 2021-12-09
  • 1970-01-01
  • 2015-01-15
  • 1970-01-01
  • 2011-07-15
  • 2013-04-04
相关资源
最近更新 更多