#c #multithreading
Вопрос:
Я совершенно новичок в многопоточности, и у меня есть небольшие проблемы с пониманием того, как на самом деле работает многопоточность.
Давайте рассмотрим следующий пример кода. Программа просто принимает имена файлов в качестве входных данных и подсчитывает количество строчных букв в них.
#include <iostream>
#include <thread>
#include <mutex>
#include <memory>
#include <vector>
#include <string>
#include <fstream>
#include <ctype.h>
class LowercaseCounter{
public:
LowercaseCounter() :
total_count(0)
{}
void count_lowercase_letters(const std::stringamp; filename)
{
int count = 0;
std::ifstream fin(filename);
char a;
while (fin >> a)
{
if (islower(a))
{
std::lock_guard<std::mutex> guard(m);
total_count;
}
}
}
void print_num() const
{
std::lock_guard<std::mutex> guard(m);
std::cout << total_count << std::endl;
}
private:
int total_count;
mutable std::mutex m;
};
int main(){
std::vector<std::unique_ptr<std::thread>> threads;
LowercaseCounter counter;
std::string line;
while (std::cin >> line)
{
if (line == "exit")
break;
else if (line == "print")
counter.print_num(); //I think that this should print 0 every time it's called.
else
threads.emplace_back(new std::thread(amp;LowercaseCounter::count_lowercase_letters, counter, line));
}
for (autoamp; thread : threads)
thread->join();
}
Во-первых, я подумал, что вывод counter.print_num()
будет выводить 0, если потоки еще не «соединены», чтобы выполнять функции. Однако, оказывается, что программа работает правильно и вывод counter.print_num()
не равен 0. Поэтому я задал себе следующие вопросы.
Что на самом деле происходит, когда создается поток?
Если вышеприведенная программа работает нормально, то поток должен быть выполнен при создании, тогда что делает std::thread::join
метод?
Если поток выполняется во время создания, то какой смысл использовать многопоточность в этом примере?
Заранее спасибо.
Комментарии:
1. Похоже, вы думаете, что нить больше похожа на сопрограмму. Потоки начинают выполняться при создании и
join()
ждут, пока поток не закончится . Кроме того, он работает параллельно всем другим потокам, возможно, на другом ядре процессора.2. @Frank Если поток имеет указанное время начала (при построении), то я полагаю, что у него должно быть указанное время окончания . Что вы имеете в виду, когда говорите
join()
«ждет, пока поток не закончится»?3. Поток просто заканчивается, как только возвращается функция точки входа, точно так же, как программа заканчивается, когда она достигает конца
main()
.4. @Frank Итак, что бы произошло, если бы я не вызывал
join()
метод для каждого из потоков.
Ответ №1:
У вас, похоже, сложилось впечатление, что программа может запускать только один поток за раз и что ей необходимо прерывать все, что она делает, чтобы выполнить код потока. Это не тот случай.
Вы можете думать о потоке как о совершенно отдельной программе, которая делится памятью и ресурсами с программой, ее создавшей. Функция, которую вы передаете в качестве аргумента, является «main ()» этой программы для каждого намерения и цели. В Linux потоки-это буквально отдельные процессы, но что касается C , то это всего лишь деталь реализации.
Таким образом, в современной операционной системе с упреждающей многозадачностью, как и несколько программ, которые могут выполняться одновременно, потоки также могут выполняться одновременно. Обратите внимание, что я говорю «может«, компилятор и ОС сами решают, когда выделять процессорное время каждому потоку.
тогда что делает метод std::thread::join?
Он просто ждет, пока поток не завершится.
Итак, что произойдет, если я не вызову метод join() для каждого из потоков
Это приведет к сбою по достижении конца main()
, потому что попытка выйти из программы, не присоединившись к несоединенному потоку, считается ошибкой.
Ответ №2:
Как вы сказали, в c поток выполняется при его создании, все std::thread::join
, что нужно, — это дождаться завершения выполнения потока.
В вашем коде все потоки начнут выполняться одновременно в цикле, а затем основной поток будет ждать, пока каждый поток завершит выполнение в следующем цикле.
Комментарии:
1. Если поток имеет указанное время начала(при построении), то я полагаю, что у него должно быть указанное время окончания. Что вы имеете в виду, когда говорите, что join() ждет окончания потока?
2. @KarenBaghdasaryan Почему у потока должно быть указанное время окончания? Предположим, что поток вызывает функцию, которая завершается только тогда, когда запас превышает определенное значение, основной поток не должен знать, когда другой поток завершит выполнение. Теперь, если бы основной поток хотел выполнить код только после завершения этого потока, вы бы вызвали
join
этот поток и дождались его завершения. При суммированииjoin
в основном приостанавливается основной поток до тех пор, пока объединенный поток не завершит выполнение.3. О, большое вам спасибо!
4. Вы имеете в виду, приостанавливает поток, вызвавший соединение, до тех пор, пока объединенный поток не завершится. Любая нить может присоединиться к другой. Основная нить не имеет ни к чему отношения.