многопоточность — вызов метода с потоком в переполнении стека

Как вызвать метод класса с потоком?

Мои занятия:

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;
}

Звоню с основного, только выкладываю соответствующую часть. csv — это вектор имен файлов, в основном вектор строк.

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 wrapper из объекта, я получаю эту ошибку:

terminate called after throwing an instance of 'std::system_error' what(): Enable multithreading to use std::thread: Operation not permitted

Теперь обновлено до g ++ — 5, без ref-оболочки и снова ошибка:
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'

1

Решение

Вот один из способов решения насущных проблем, которые я могу найти в вашем коде. Я также добавил std:: везде, потому что использование пространства имен 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();
}

[2]

Давайте начнем здесь, потому что это самая прямая проблема. Шаблон для вызова функции-члена с помощью std :: thread:

std::thread(ptr-to-member-func, ptr-to-object, member-func-args...);

По сути вы должны предоставить объект, который станет this при выполнении loaddata(), И это называется «указатель» по причине. 😉

Также emplace_back() является функцией пересылки, которая принимает только аргументы конструктора объекта, который вы хотите поместить в вектор. Явное построение std::thread(...) здесь побеждает цель.

[3]

Это тоже просто. Вы должны убедиться, что потоки могут выполнять всю свою работу и правильно выходить. Это значит звонить join() в какой-то момент, который будет блокировать, пока поток не будет завершен. Это особенно важно, если у вас все еще работают потоки, когда вы хотите выйти из всей программы.

Обратите внимание, что вызов join() внутри цикла for, как в ответе ΔλЛ, не сработает. Если вы сделаете это так, вы создадите один поток, дождитесь его завершения, а затем перейдите к следующему потоку. Это запутанный способ выполнения однопоточного исполнения.

[1]

Небольшое напоминание о том, как работает вектор (и в основном все остальные контейнеры STD). Он выделяет блок памяти и использует его для предметов, которые вы push_back(), Когда ему не хватает места, он выделяет новый — больший — блок памяти, копирует или перемещает туда существующие элементы и продолжает push_back(), Перераспределение означает, что все ссылки и указатели на элементы, а также все итераторы в векторе недействительны.

Что возвращает нас к [2]. Там вы передаете указатель на элемент в векторе ts, Но ts будут придется перераспределять в какой-то момент во время цикла (по крайней мере, это то, что вы должны предположить) — оставляя потоки с висячими указателями. Это классическое неопределенное поведение.

Есть несколько способов решить эту проблему. [1] показывает очень простой пример, который хорошо работает, если вы заранее знаете, насколько большим будет вектор. reserve() выделяет достаточно места для заданного количества элементов, что предотвращает перераспределение. Ваши указатели остаются в силе.

Другим решением является введение дополнительной косвенности: обычно путем размещения элементов в куче и сохранения указателей в векторе. Как это:

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 объекты больше не затрагиваются.

[1a]

Почему я сказал, что это необязательно? th собирается перераспределить так же, как ts, Тем не менее, вы не держитесь за ссылки или ссылки на ваши объекты потока. Неважно, если они будут признаны недействительными. А также std::thread подвижный, то есть он прекрасно переживет перераспределение. С другой стороны, выделение памяти является потенциально дорогостоящей операцией, и в этом случае ее тривиально избежать.

Постскриптум

Посмотри на std::async для более высокого уровня подхода к параллелизму. Потоки — это конструкция очень низкого уровня. Если вы можете, лучше избегать прямого обращения с ними.

0

Другие решения

Вы создаете свой указатель как временную переменную, размещенную в стеке, и emplace_back это в ваш вектор. Когда вы выходите из for итерация цикла, дескриптор выполняется и 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 ваши темы после окончания загрузки данных.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector