如何用线程调用类方法?
我的课程:
vector<series> ts;
int col = 0;
vector<thread> th;
for (unsigned int i = 0; i != csv.size(); ++i) {
ts.push_back(series(csv[i]));
th.emplace_back(thread(&series::loaddata, ref(ts[i]), col));
}
从main调用它,仅发布相关部分。 csv是文件名的向量,基本上是字符串的向量。
/usr/include/c++/4.8/functional: In instantiation of ‘struct std::_Bind_simple<std::_Mem_fn<int (series::*)(int)>(std::reference_wrapper<series>, int)>’:
/usr/include/c++/4.8/thread:137:47: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = int (series::*)(int); _Args = {std::reference_wrapper<series>, int}]’
/home/d066537/ClionProjects/correlation/src/main.cpp:105:66: required from here
/usr/include/c++/4.8/functional:1697:61: error: no type named ‘type’ in ‘class std::result_of<std::_Mem_fn<int (series::*)(int)>(std::reference_wrapper<series>, int)>’
typedef typename result_of<_Callable(_Args...)>::type result_type;
^
/usr/include/c++/4.8/functional:1727:9: error: no type named ‘type’ in ‘class std::result_of<std::_Mem_fn<int (series::*)(int)>(std::reference_wrapper<series>, int)>’
_M_invoke(_Index_tuple<_Indices...>)
给出了我无法理解的错误。
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lpthread -std=c++11")
解决方案,请使用Clion CMake进行程序,并且是线程适用于自由函数,所以我不认为这是任何编译器标志的问题。
terminate called after throwing an instance of 'std::system_error' what(): Enable multithreading to use std::thread: Operation not permitted
如果我从对象中删除ref包装器,我会收到此错误:
`std::thread::thread<int (series::*)(int), series&, int>(int (series::*&&)(int), series&, int&&)':
/usr/include/c++/5/thread:137: undefined reference to `pthread_create'
现在更新为g ++ - 5,没有ref包装,再次出现错误: CMakeFiles / correlation.dir / src / main.cpp.o:在函数
中select top 1 company_name,
count(*) as nbr_of_employees
from work-for
group by company_name
order by 2 desc
答案 0 :(得分:0)
您正在将指针创建为临时堆栈分配变量,并将emplace_back
创建到向量中。当你离开for
循环迭代时,执行desctrutor并调用std::terminate
来终止正在运行的线程。
解决此问题的一种方法是使用std::vector<std::thread*>
代替:
vector<thread*> th;
for (unsigned int i = 0; i != csv.size(); ++i) {
ts.push_back(series(csv[i]));
thread t = new thread(&series::loaddata, ref(ts[i]), col)
th.emplace_back(t);
th[i]->join();
}
在完成加载数据后,请记住delete
您的主题。
答案 1 :(得分:0)
这是解决我在代码中可以找到的直接问题的一种方法。我还在std::
添加了std::vector<series> ts;
ts.reserve(csv.size()); // [1]
int col = 0;
std::vector<std::thread> th;
th.reserve(csv.size()); // [1a] optional
for (unsigned int i = 0; i != csv.size(); ++i) {
ts.push_back(series(csv[i]));
th.emplace_back(&series::loaddata, &ts[i], col); // [2]
}
// Lots of other code could be executed here. Joining only has to happen
// at *some* point after creating the threads.
// [3]
for (auto& thread : th) {
thread.join();
}
因为using namespace std is bad。
std::thread(ptr-to-member-func, ptr-to-object, member-func-args...);
让我们从这里开始,因为这是最直接的问题。使用std :: thread调用成员函数的模式是:
this
基本上,您必须在执行loaddata()
时提供将成为emplace_back()
的对象。它被称为“这个指针”是有原因的。 ;)
同样std::thread(...)
是一个转发函数,它只接受你想要放入向量的对象的构造函数参数。在这里明确地构建join()
会破坏目的。
这个也很简单。您必须确保线程可以完成所有工作并正确退出。这意味着在某个时刻调用join()
,这将阻塞直到线程完成。如果你想在退出整个程序时仍然运行线程,这一点尤其重要。
请注意,在ΔλЛ的答案中调用for循环中的push_back()
将不起作用。如果您这样做,您将创建一个线程,等待它完成,然后继续下一个线程。这是一种进行单线程执行的复杂方式。
关于vector(以及基本上所有其他std容器)如何工作的小提醒。它分配一块内存并将其用于push_back()
项。当空间不足时,它会分配一个新的 - 更大 - 的内存块,复制或移动现有项目并继续ts
。重新分配意味着所有引用和指向项目的指针以及向量中的所有迭代器都将失效。
这让我们回到了[2]。在那里,您传递一个指向向量ts
中的项目的指针。但是reserve()
将必须在循环期间的某个时刻重新分配(至少这是你必须假设的) - 让线程留下悬空指针。这是经典的未定义行为。
有多种方法可以解决这个问题。 [1]显示了一个非常简单的例子,如果你事先知道矢量有多大,它就可以很好地工作。 std::vector<std::unique_ptr<series>> ts;
for (...) {
ts.push_back(new series(csv[i]));
// In C++14 you would use std::make_unique instead of a raw new.
th.emplace_back(
&series::loaddata,
ts[i].get(), // notice the difference
col);
}
为给定数量的元素分配足够的空间,这会阻止重新分配。你的指针保持有效。
另一个解决方案是引入一个额外的间接:通常通过在堆上分配项并在向量中存储指针。像这样:
series
现在,矢量可以根据需要重新分配。它只需要复制一些指向其新内存块的指针。实际th
对象的地址不再受影响。
为什么我说这是可选的? ts
将重新分配std::thread
。但是,您不会保留任何引用线程对象的引用或指针。如果它们失效并不重要。并且std::async
是可移动的,即它将在重新分配中存活得很好。另一方面,内存分配是一项潜在的昂贵操作,在这种情况下可以避免这种情况。
查看ggplot
以获得更高级别的并发方法。线程是一个非常低级的构造。如果可以,最好避免直接处理它们。