могу ли я его тут задать?
Да
Надеюсь что да) У меня такой вопрос https://pastebin.com/swHkE4Vj Начал изучать многопоточность и пытаюсь сделать суммирование столбцов матрицы, каждый в параллель, но что-то мудрю-мудрю уже сутки и не могу понять до конца как правильно нужно работать с async/future или thread (через thread получилось, но он только один, а я хочу чтобы каждый столбец матрицы считался параллельно, те в рантайме определял сколько столбцов - столько и потоков) Может кто-нибудь помочь, да направить на путь истинный? P.S. Делаю изначально считывание из файла, но там в main заккоментил
Мьютекс для чего?
Задача просуммировать значения в каждом столбике и вывести сумму для каждого?
std::thread th1(...); th1.join() Вот в этом по факту смысла нет - получается синхронное выполнение функции потока. А вообще если хочется, чтобы параллельно считались суммы по столбцам, то в функцию потока и надо впихивать тогда один столбец, который суммируется в этом потоке
Ну как я понял его нужно для синхронизации использовать при доступе к одному объекту, те к моей матрице
Если конкурентно только читаешь, то синхронизация не нужна
А ты что читаешь за книгу?
Энтони Уильямс + интернет + хабр
Пытаюсь на практике тыкаться потихоньку
Кроме того, мьютекс (или иной примитив синхронизации) не должен быть локальным для потока, как в твоём случае. В ColumnSum просто мьютекс на стеке. От него смысла опять же нет никакого - в других потоках о нем не знают
То есть он всё равно считает каждый столбец в одном потоке?
Ну ты запускаешь поток и сразу же блокируешь вызывающий поток до завершения созданного
У тебя тут же доступа нет к одному объекту. Тут у тебя потоки каждый на своем столбце и никто одновременно одну ячейку не изменяет вместе с другим потоком. Тут проблема конечно с кэшами, но не думаю что сильно волнует сейчас
Кажется понял) То есть пока не завершится вычисление одного столбца - остальные ждут?
Знаешь что джоин делает?
Остальные, более того, и не создаются даже ещё join - блокирующая операция
Закрывает потоки, те блокирует как я понял и потом закрывает
Джоин ждёт завершения потока
Нет. Вот есть у тебя вызывающий join поток и сторонний поток (для которого этот join вызывается) join блокирует вызывающий поток до тех пор, пока не завершится тот сторонний поток поток А на любые другие потоки, очевидно, никакого влияния нет
https://pastebin.com/ut7TBJYU
Я вот подправил только что, сделал вектор тредов чтобы запустить в одно время
Ага, то есть на 25-й строчке threads.emplace_back(ColumnSum, matrix, i); надо заменить на: threads.emplace_back(ColumnSum, std::cref (matrix), std::cref(i));
Ну вот это уже ок (но про cref глянь) Только вот фьючи там зачем - неясно. К слову, любое непойманное исключение внутри функции потока для std::thread - вызов std::terminate
Ээээ, i зачем по ссылке то?
но у меня в сигнатуре она тоже const & Да и почему нет? Зачем лишнее копирование?
Для тривиальных типов (да и тех, что в регистр влезают) копирование - одна машинная инструкция И их принято как раз по значению передавать
Спасибо! Тогда ещё вопрос: У меня там закоменчено с async и оно падало с ошибкой. Как вообще лучше (и правильнее) его использовать?
У меня просто вижуалка закрывалась)))
А почему они не нужны? (фьючи)
Ты же понимаешь что у всех тредов будет ссылка на одну протухшую переменную? При cref
Ну потому что у тебя и так задача в тредах выполняется
А вот, кстати, поэтому и падает std::async скорее всего Ибо захват i в лямбду по ссылке идёт, а дальше уже UB
Не знаю что там конечно падать может. Просто хз что выведет в итоге
0_0 Да, матрицу по ссылке - ок, а i - не ок (упал с ошибкой) Можете подробнее объяснить?
Ну UB на то и UB
Не знаю просто из-за чего упасть может 🤷 и что там происходит такого
Там, по выходе из цикла i больше нет, а вот лямбды ещё жить могут А ссылка на i в них живёт
Хотя. Там дальше ещё один цикл
Как вариант async решает выполниться отложенно (когда get позовут), а там ссылка на i в лямбдах
Там же i не меняется а просто читается. Там просто мусор
Ну читать по битой ссылке - UB Хотя по идее i жила на стеке, адрес на стек и будет указывать... Доступ для чтения там будет. Впрочем, опять рассуждения о том, что при UB происходит
Так, треды вроде заполняются и теперь всё оки, а как теперь мне результаты из вектора тредов записать в vector<int>?
Вручную использовать std::future/std::promise std::thread игнорирует результат функции Ну или выходные параметры функции прикрутить... Но это такая себе история, лучше не надо)
Можно на моём примере, если не сложно Не очень с фьючерами понимаю как это сделать, копнул только чуть-чуть
Чекаем примеры в доке: https://en.cppreference.com/w/cpp/thread/future Кстати, там же ещё про std::packaged_task написано. Оно чуть больше подходит
Буду разбираться, спасибо! А можете литературу посоветовать ещё хорошую, где, особенно много примеров
То ли в каком-то закрепе, то ли в описании чата есть ссылка на книжки По параллельщине - ты правильную книжку выбрал - ту, что от Вильямса
Принято, буду пытаться дальше)
std::vector<int> result; std::vector<std::future<int>> futures; // futures.reserve(matrix.size()); std::vector<std::thread> threads; for (size_t i = 0; i < matrix[0].size(); i++) { std::packaged_task<int(const std::vector<std::vector<int>>&, int)> task(ColumnSum); // std::future<int> result1 = task.get_future(); std::future<int> ret = task.get_future(); threads.emplace_back(std::move(task)); futures.push_back(ret); //threads.emplace_back(ColumnSum, std::cref(matrix), i); } Пытаюсь делать по примеру из доки, но падает с ошибкой: C2280 'std::future<int>::future(const std::future<int> &)': attempting to reference a deleted function По гуглу не понимаю в чём ошибка
std::future некопируемый тип, о чем тебе и сказано в ошибке А push_back создает элемент, а потом в него копирует аргумент
И как быть тогда?
futures.emplace_back(std::move(ret)); Чем вариант строкой выше не понравился?
У тебя мьютекс, он не нужен в данном случае, так как матрица read only в процессе вычисления
Error C2672 'invoke': no matching overloaded function found Error C2893 Failed to specialize function template 'unknown-type std::invoke(_Callable &&) noexcept(<expr>)
Ещё, если бы мьютекс был нужен, его надо было бы делать один на всю матрицу скорее всего
Спасибо, мне этот момент уже подсказали!
Ещё что-то не ясно ?
Да, вылезли новые проблемы
std::vector<int> result; std::vector<std::future<int>> futures; // futures.reserve(matrix.size()); std::vector<std::thread> threads; for (size_t i = 0; i < matrix[0].size(); i++) { std::packaged_task<int(const std::vector<std::vector<int>>&, int)> task(ColumnSum); // std::future<int> result1 = task.get_future(); std::future<int> ret = task.get_future(); threads.emplace_back(std::move(ret)); // futures.push_back(ret); //threads.emplace_back(ColumnSum, std::cref(matrix), i); }
Error C2672 'invoke': no matching overloaded function found Error C2893 Failed to specialize function template 'unknown-type std::invoke(_Callable &&) noexcept(<expr>)
https://pastebin.com/baRjd5sf
Тип ColumnSum не совпадает с тем, что в параметре шаблона таски указан
Да, блин, я поправил, но всё та же ошибка
У тебя там мракобесие с векторами получилось. Во-первых, фьючи куда-то не туда складируются Во-вторых, а я чет не вижу запуска тасок Ну да, ты в threads зачем-то кладешь фьючи. std::thread конструируется от std::future и пытается вызвать её (sic!). Ясно, что std::future - ни разу не Callable, вот и ошибка
А зачем тебе сразу и треды, и фьючи?
Тренируется человек и так и так делать помеременно
Ты это как, по наитию писал или как? Почему именно так?
Пытаюсь освоить многопоточку, чтобы оно заработало, разные варианты перебираю...
Там их два всего., либо треды, либо фьючи
Ага, а как мне тогда заполнить vector<int> результатами из threads?
Да, кажется понял что-то из вашего сообщения Но как мне тогда из треда получить значение и записать его в вектор? Вы говорили как-то future отдельно дёргать, вот я и попытался Создал таску в package, task.get_future(); по идее должен получаю его значение? и потом в вектор пытаться? Но тогда это же будет один поток...
У фьючи есть метод get, который и возвращает значение, полученное из другого потока, из другой части программы, из космоса, etc. Идея в чем: создаем таски, получаем фьючи, запускаем таски в потоках, джойним потоки, а дальше примерно так: std::vector<int> results; std::transform(futures.cbegin(), futures.cend(), std::back_inserter(results), [](const auto& f) { return f.get(); }); reserve ещё стоит вызвать
std::packaged_task<int(const std::vector<std::vector<int>>&, const int&)> task(ColumnSum); std::future<int> res1 = task.get_future(); std::thread t(task(std::cref(matrix), i)); Так? Ошибки те же самые... Создали таску, получили фьючу, пытаемся получить фьючу и отправить в поток
Храни тебя господь, поставлю свечку за тебя Что-то получилось с таким кодом: std::packaged_task<int(const std::vector<std::vector<int>>&, const int&)> task(ColumnSum); futures.push_back(task.get_future()); threads.emplace_back(std::move(task),std::cref(matrix), i);
Но пока что только один поток...
https://github.com/progschj/ThreadPool самый простой
Обсуждают сегодня