c++ - 在 C++ 中使用线程调用方法

标签 c++ multithreading class-method

如何用线程调用类方法?

我的类(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/

相关文章:

c++ - 用纯虚拟类 copy-and-swap 成语

C++:方法中的字符串成员别名

c++ - 如何用C++给定的地址覆盖汇编程序栈的返回地址?

java - 流媒体在线广播,主线程上的工作太多

Android 消息处理程序未收到我发送的所有消息

java - ExecutorService 的 future 返回值

Xcode 4.6 "No visable @interface for ' PlayingCardDeck'声明选择器 'addCard:AtTop:'

python - 当原始类的实例由另一个类的方法生成时,如何处理子类?

c++ - 嵌套的匿名命名空间

c++ - libavcodec,如何对不同帧率的视频进行转码?