如何用线程调用类方法?
我的类(class):
using namespace std;
class series{
private:
string filename;
vector<double> data;
public:
series(string _filename);
int loaddata(const int col);
void readdata() const;
};
series::series(string _filename):filename(_filename) {}
int series::loaddata(const int col)
{
ifstream input(filename);
string line;
if (!input) {
cout << "File failed to open" << endl;
return 0;
}
while(!input.eof())
{
while(getline(input, line)){
vector<string> oneline;
boost::split(oneline, line, boost::is_any_of("|"));
data.push_back(boost::lexical_cast<double>(oneline[col]));
}
}
return 1;
}
从 main 调用它,只发布相关部分。 csv 是文件名 vector ,基本上是字符串 vector 。
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));
}
给出了我无法理解的错误。
/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...>)
请解决方案,为程序使用 Clion CMake,是的,线程适用于免费功能,所以我认为这与任何编译器标志无关。
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lpthread -std=c++11")
如果我从对象中删除 ref 包装器,我会收到此错误:
terminate called after throwing an instance of 'std::system_error' what(): Enable multithreading to use std::thread: Operation not permitted
现在更新到 g++-5,没有 ref wrapper 并且再次出现错误: CMakeFiles/correlation.dir/src/main.cpp.o: 函数中
`std::thread::thread<int (series::*)(int), series&, int>(int (series::*&&)(int), series&, int&&)':
/usr/include/c++/5/thread:137: undefined reference to `pthread_create'
最佳答案
这是解决我在您的代码中发现的紧迫问题的一种方法。我还在各处添加了 std::
因为 using namespace std is bad .
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();
}
[2]
让我们从这里开始,因为这是最直接的问题。使用 std::thread 调用成员函数的模式是:
std::thread(ptr-to-member-func, ptr-to-object, member-func-args...);
本质上,您必须提供在执行 loaddata()
时将变为 this
的对象。它被称为“this 指针”是有原因的。 ;)
此外,emplace_back()
是一个转发函数,它只接受要放入 vector 的对象的构造函数参数。在这里显式构造 std::thread(...)
达不到目的。
[3]
这个也很简单。您必须确保线程可以完成所有工作并正确退出。这意味着在某个时候调用 join()
,这将阻塞直到线程完成。如果在您想要退出整个程序时仍有线程在运行,这一点尤其重要。
请注意,像 ΔλЛ 的回答那样在 for 循环内调用 join()
是行不通的。如果你这样做,你将创建一个线程,等待它完成,然后继续下一个线程。这是一种复杂的单线程执行方式。
[1]
关于 vector(以及基本上所有其他 std 容器)如何工作的小提示。它分配一 block 内存并将其用于您 push_back()
的项目。当它用完空间时,它会分配一个新的——更大的——内存块,复制或移动现有的项目并继续 push_back()
。重新分配意味着指向项的所有引用和指针以及 vector 中的所有迭代器都将失效。
这让我们回到 [2]。在那里,您传递一个指向 vector ts
中的项目的指针。但是 ts
将 必须在循环期间的某个时刻重新分配(至少这是你必须假设的)——让线程带有悬空指针。这是典型的未定义行为。
有多种方法可以解决这个问题。 [1] 展示了一个非常简单的方法,如果您事先知道 vector 将变得有多大,它就可以很好地工作。 reserve()
为给定数量的元素分配足够的空间,从而防止重新分配。您的指示仍然有效。
另一种解决方案是引入额外的间接寻址:通常是通过在堆上分配项目并将指针存储在 vector 中。像这样:
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);
}
现在 vector 可以随心所欲地重新分配。它只需要将一些指针复制到它的新内存块。实际 series
对象的地址不再受到影响。
[1a]
为什么我说这是可选的? th
将重新分配与 ts
相同的内容。但是,您不会保留任何指向线程对象的引用或指针。他们是否无效并不重要。并且 std::thread
是可移动的,即它将在重新分配后继续存在。另一方面,内存分配是一个潜在的昂贵操作,在这种情况下避免它是微不足道的。
附言
查看 std::async
以获得更高级的并发方法。线程是一个非常低级的结构。如果可以,最好避免直接与他们打交道。
关于c++ - 在 C++ 中使用线程调用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44243306/