2

我目前正在使用 C++ 开发一个项目,在该项目中,我有一个存储在向量中的结构列表,其中有很多与它们相关的处理。为了加快速度,我选择将程序拆分为多个线程,而我选择这样做的懒惰方式是向标准库向量中的每个结构添加一个互斥体。这样,我可以让多个线程遍历数组,并通过调用 mutex.try_lock() 来获取各个元素的所有权,并完成与该元素的关联处理,或者移至下一个打开的元素。

在我们开始之前,请注意以下内容确实有效。

#include <mutex>
#include <vector>

struct foo {
    int a;
    std::mutex b;
};

void working_demo () {
    // assign by initializer list
    foo f = {.a = 1};
}

int main () {
    working_demo();
}

所以,我打算以与上面非常相似的方式填充我的标准向量,但它不起作用。

#include <mutex>
#include <vector>

struct foo {
    int a;
    std::mutex b;
};

void broken_demo () {
    std::vector<foo> bar;

    // assign by initializer list
    bar.push_back({.a = 1});
}

int main () {
    broken_demo();
}

编译器错误:

clang++ -std=c++11 -Wall -Wextra -Wfatal-errors -pedantic -I./  -c -o demo.o demo.cpp
demo.cpp:13:20: warning: designated initializers are a C99 feature [-Wc99-extensions]
    bar.push_back({.a = 1});
                   ^~~~~~
In file included from demo.cpp:1:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/mutex:38:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/tuple:39:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/array:39:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/stdexcept:39:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/string:41:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/allocator.h:46:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/x86_64-pc-linux-gnu/bits/c++allocator.h:33:
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/ext/new_allocator.h:146:8: fatal error: call to implicitly-deleted copy constructor of 'foo'
                            _Up(std::forward<_Args>(__args)...)))
                            ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/alloc_traits.h:483:24: note: in instantiation of exception specification for
      'construct<foo, foo>' requested here
        noexcept(noexcept(__a.construct(__p, std::forward<_Args>(__args)...)))
                              ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/vector.tcc:115:21: note: in instantiation of exception specification for 'construct<foo, foo>'
      requested here
            _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
                           ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_vector.h:1201:9: note: in instantiation of function template specialization
      'std::vector<foo, std::allocator<foo> >::emplace_back<foo>' requested here
      { emplace_back(std::move(__x)); }
        ^
demo.cpp:13:9: note: in instantiation of member function 'std::vector<foo, std::allocator<foo> >::push_back' requested here
    bar.push_back({.a = 1});
        ^
demo.cpp:6:16: note: copy constructor of 'foo' is implicitly deleted because field 'b' has a deleted copy constructor
    std::mutex b;
               ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/std_mutex.h:94:5: note: 'mutex' has been explicitly marked deleted here
    mutex(const mutex&) = delete;
    ^

我相当肯定这是说这不起作用的原因是该结构试图调用互斥体的复制构造函数,而实际上它没有复制构造函数。我明确不想这样做。

我最初的想法是,为了确保它甚至不会尝试调用互斥锁的复制构造函数,我可以为我的类创建自己的构造函数,并且基本上明确地省略了互斥锁的复制。这种方法看起来像这样 -但它也不起作用。

#include <mutex>
#include <vector>

struct foo {
    foo (int A): a(A) {;}

    int a;
    std::mutex b;
};

void broken_demo () {
    std::vector<foo> bar;

    // assign by initializer list
    bar.emplace_back(1);
}

int main () {
    broken_demo();
}

编译器错误:

clang++ -std=c++11 -Wall -Wextra -Wfatal-errors -pedantic -I./  -c -o demo.o demo.cpp
In file included from demo.cpp:2:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/vector:65:
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_construct.h:75:38: fatal error: call to implicitly-deleted copy constructor of 'foo'
    { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
                                     ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_uninitialized.h:83:8: note: in instantiation of function template specialization
      'std::_Construct<foo, foo>' requested here
                std::_Construct(std::__addressof(*__cur), *__first);
                     ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_uninitialized.h:134:2: note: in instantiation of function template specialization
      'std::__uninitialized_copy<false>::__uninit_copy<std::move_iterator<foo *>, foo *>' requested here
        __uninit_copy(__first, __last, __result);
        ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_uninitialized.h:289:19: note: in instantiation of function template specialization
      'std::uninitialized_copy<std::move_iterator<foo *>, foo *>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                  ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_uninitialized.h:310:19: note: in instantiation of function template specialization
      'std::__uninitialized_copy_a<std::move_iterator<foo *>, foo *, foo>' requested here
      return std::__uninitialized_copy_a
                  ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/vector.tcc:473:10: note: in instantiation of function template specialization
      'std::__uninitialized_move_if_noexcept_a<foo *, foo *, std::allocator<foo> >' requested here
                = std::__uninitialized_move_if_noexcept_a
                       ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/vector.tcc:121:4: note: in instantiation of function template specialization 'std::vector<foo,
      std::allocator<foo> >::_M_realloc_insert<int>' requested here
          _M_realloc_insert(end(), std::forward<_Args>(__args)...);
          ^
demo.cpp:15:9: note: in instantiation of function template specialization 'std::vector<foo, std::allocator<foo> >::emplace_back<int>' requested here
    bar.emplace_back(1);
        ^
demo.cpp:8:16: note: copy constructor of 'foo' is implicitly deleted because field 'b' has a deleted copy constructor
    std::mutex b;
               ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/std_mutex.h:94:5: note: 'mutex' has been explicitly marked deleted here
    mutex(const mutex&) = delete;
    ^
1 error generated.

关于解决这个问题的任何想法?我希望保持代码相对简单,但我不确定如何使标准向量运行,或者至少正确使用它。

4

2 回答 2

1

在查看了其中一个解决方案之后,我认为实际上这个解决方案最适合我的目标。如果没有上述有用的建议,我将无法弄清楚这一点。

#include <mutex>
#include <vector>

struct foo {
    int a;
    std::mutex b;

    foo (int a) : a(a) {;} 
    foo (const foo &f) : a(f.a) {;}
};

void other_fixed_demo () 
{
    std::vector<foo> bar;
    bar.emplace_back(1);
}

int main () {
    other_fixed_demo();
}

我不能说为什么,但显然这里发生了两件事。首先,我们使用1输入调用构造函数,然后在某个地方emplace_back()调用复制构造函数。两者都需要在创建结构的每种方式中明确地不包含互斥锁,而上述解决方案避免了这种情况。

于 2019-11-19T00:49:15.900 回答
1

问题是在您的代码实例中foo是按值传递的。因此,当您将某些内容放入向量中时,需要创建一个副本。避免这种情况的一种简单方法是将指针foo放入向量中。您可以将指针包装到一些引用计数机制中,这样您就不必跟踪释放对象。

这是一个简短的示例,当向量超出范围时std::unique_ptr,它将自动删除向量中的所有实例:foo

#include <mutex>
#include <vector>
#include <memory>
#include <iostream>
#include <functional>

struct foo {
   foo(int A) : a(A) {}
   int a;
   std::mutex b;
};

void fixed_demo () {
   std::vector<std::unique_ptr<foo>> bar;

   std::unique_ptr<foo> p(new foo(1));
   bar.push_back(std::move(p));
   bar.emplace_back(new foo(2));
   for (auto it = bar.begin(); it != bar.end(); ++it) {
      (*it)->b.lock();
      std::cout << (*it)->a << std::endl;
      (*it)->b.unlock();
   }
}

int main () {
   fixed_demo();
   return 0;
}
于 2019-11-18T08:05:36.990 回答