Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.91/11: Рейтинг темы: голосов - 11, средняя оценка - 4.91
32 / 21 / 4
Регистрация: 18.11.2012
Сообщений: 955
1

Thread. Многопоточность

10.08.2019, 16:31. Показов 1914. Ответов 26

Привет! есть код, код не мой, но хотелось бы разобраться, по возможности. Нужно генерировать строки
в диапазоне [0, 100)
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
mutex mtx;
 
class Make_timer
{
public:
    Make_timer() :t1(system_clock::now()) { }
    void reset() { t1 = system_clock::now(); }
 
    void operator()(const string& label)
    {
        auto t2 = system_clock::now();
        cout << "  " << label << " tool: "
            << duration_cast<milliseconds>(t2 - t1).count() << " ms.\n";
    }
private:
    system_clock::time_point t1;
};
 
 
string get_string()
{
    const unsigned int min = 1;
    const unsigned int max = 100;
    const unsigned int low = 33;
    const unsigned int high = 126;
 
    stringstream ss;
    thread_local static default_random_engine ran;
    auto len = uniform_int_distribution<>{ (int)min, (int)max }(ran);
 
    for (auto i = 0; i < len; ++i)
    {
        ss << uniform_int_distribution<>{(int)low, (int)high}(ran);
    }
    return ss.str();
}
 
template<typename C>
C random_fill(int n)
{
    C vs;
    for (auto i = 0; i < n; ++i)
        vs.insert(get_string());
    return vs;
}
 
 
void fill_task(set<string>& vs, int n, mutex& m)
{
    set<string> v = random_fill<set<string>>(n);
 
    unique_lock<mutex> lck(m);
    //copy(v.begin(), v.end(), back_inserter(vs));
    for (const auto& a : v)
        vs.insert(a);
}
 
set<string> threaded_fill(int n)
//fill a large vector with random strings.
{
    set<string> vs;
    const int num_threads = 8;
    
    //const int num_threads = thread::hardware_concurrency();
 
    //cout << "number of threds " << num_threads << "\n";
 
    mutex mtx;
    vector<thread> vt;
    for (auto i = 0; i < num_threads; ++i)
        vt.push_back(thread(fill_task, ref(vs), n / num_threads, ref(mtx)));
 
    for (auto& t : vt)
        t.join();
 
    cout << "vector size: " << vs.size() << '\n';
 
    return vs;
}


//Здесь код из функции main():
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
    //....
const int max1 = 500000;
        const int max2 = 5000000;
    Make_timer timer;
    cout << "Filling both vectors..\n";
    timer.reset();
    //vector<string> vs1 = random_fill(max1);
    //vector<string> vs2 = random_fill(max2);
    set<string> vs1 = threaded_fill(max1);
    set<string> vs2 = threaded_fill(max2);
    timer("set fills");
    //....
}
0

Помощь в написании контрольных, курсовых и дипломных работ здесь.

Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
10.08.2019, 16:31
Ответы с готовыми решениями:

Многопоточность в C++11. std::thread
Привет всем! использую таким образом многопоточность class A { public: void fun() {...

Std::thread автоматическая многопоточность
Есть данный пример создания массива thread и инициализации его в цикле. #include&lt;iostream&gt; ...

Ошибка компиляции "no instance of constructor 'std::thread::thread' matches the argument list"
Не могу сообразить почему возникает ошибка. У меня в классе есть метод, который должен работать в...

Boost::thread std::thread
чем отличается boost::thread( ) от std::thread (с++17)? я спрашиваю не о способе реализации...

26
32 / 21 / 4
Регистрация: 18.11.2012
Сообщений: 955
10.08.2019, 16:45  [ТС] 2
Привет, форумчане! есть код, код не мой, но хотелось бы, по возможности, разобраться. Нужно генерировать случайные строки
из диапазоне [0, 100), а затем отсортировать их с помощью функции std::sort(). Соответственно нужно измерить время
требующееся для сортировки(генерации строк).

Почему этот код грузит процессор на 100%? А код, который не использует thread выполняется не намного медленней, но
процессор нагружает процентов на 27-30.
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
mutex mtx;
 
class Make_timer
{
public:
    Make_timer() :t1(system_clock::now()) { }
    void reset() { t1 = system_clock::now(); }
 
    void operator()(const string& label)
    {
        auto t2 = system_clock::now();
        cout << "  " << label << " tool: "
            << duration_cast<milliseconds>(t2 - t1).count() << " ms.\n";
    }
private:
    system_clock::time_point t1;
};
 
 
string get_string()
{
    const int min = 1;
    const int max = 100;
    const unsigned int low = 33;
    const unsigned int high = 126;
 
    stringstream ss;
    thread_local static default_random_engine ran;
    auto len = uniform_int_distribution<>{ min, max }(ran);
 
    for (auto i = 0; i < len; ++i)
    {
        ss << uniform_int_distribution<>{low, high}(ran);
    }
    return ss.str();
}
 
template<typename C>
C random_fill(int n)
{
    C vs;
    for (auto i = 0; i < n; ++i)
        vs.insert(get_string());
    return vs;
}
 
 
void fill_task(set<string>& vs, int n, mutex& m)
{
    set<string> v = random_fill<set<string>>(n);
 
    unique_lock<mutex> lck(m);
    //copy(v.begin(), v.end(), back_inserter(vs));
    for (const auto& a : v)
        vs.insert(a);
}
 
set<string> threaded_fill(int n)
//fill a large vector with random strings.
{
    set<string> vs;
    const int num_threads = 8;
    
    //const int num_threads = thread::hardware_concurrency();
 
    //cout << "number of threds " << num_threads << "\n";
 
    mutex mtx;
    vector<thread> vt;
    for (auto i = 0; i < num_threads; ++i)
        vt.push_back(thread(fill_task, ref(vs), n / num_threads, ref(mtx)));
 
    for (auto& t : vt)
        t.join();
 
    cout << "vector size: " << vs.size() << '\n';
 
    return vs;
}


//Здесь код из функции main():
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
    //....
const int max1 = 500000;
        const int max2 = 5000000;
    Make_timer timer;
    cout << "Filling both vectors..\n";
    timer.reset();
    //vector<string> vs1 = random_fill(max1);
    //vector<string> vs2 = random_fill(max2);
    set<string> vs1 = threaded_fill(max1);
    set<string> vs2 = threaded_fill(max2);
    timer("set fills");
    //....
}


Спасибо, за внимание!
0
278 / 87 / 37
Регистрация: 10.06.2015
Сообщений: 261
10.08.2019, 16:58 3
Почему этот код грузит процессор на 100%? А код, который не использует thread выполняется не намного медленней, но
процессор нагружает процентов на 27-30.
Ты представляешь что такое thread? И зачем он нужен? Если нет - почитай, если да - то ещё раз почитай.
0
32 / 21 / 4
Регистрация: 18.11.2012
Сообщений: 955
10.08.2019, 17:26  [ТС] 4
Цитата Сообщение от Lolobotik Посмотреть сообщение
Ты представляешь что такое thread? И зачем он нужен?
Разве не для того чтобы можно было выполнять какую-либо задачу параллельно? Мне это виделось в равномерном распределении нагрузки на ядра процессора, грубо говоря!
0
6738 / 4537 / 1839
Регистрация: 07.05.2019
Сообщений: 13,725
Записей в блоге: 1
10.08.2019, 18:14 5
Цитата Сообщение от Liss29 Посмотреть сообщение
Почему этот код грузит процессор на 100%? А код, который не использует thread выполняется не намного медленней, но
процессор нагружает процентов на 27-30.
Потому что однопоточный вариант грузит только одно ядро процессора, на 100%, а многопоточный - все ядра.

Добавлено через 6 минут
Цитата Сообщение от Liss29 Посмотреть сообщение
const int num_threads = 8;
Здесь лучше сделать const size_t num_threads = std::thread::hardware_concurrency();
0
2723 / 1887 / 559
Регистрация: 05.06.2014
Сообщений: 5,499
10.08.2019, 18:21 6
Цитата Сообщение от Liss29 Посмотреть сообщение
Разве не для того чтобы можно было выполнять какую-либо задачу параллельно? Мне это виделось в равномерном распределении нагрузки на ядра процессора, грубо говоря!
Оно и распределило.
Однопоточный вариант: одно ядро нагружено на 100% на десять минут, другое простаивает. В среднем по больнице - нагрузка 50%.
Двухпоточный вариант: оба ядра нагружены на 100%, но на пять минут. В среднем по больнице - нагрузка 100%.

Ну и ваш вариант: все треды лезут в общую динамическую память, стукаются лбами и половина времени уходит на синхронизацию этого процесса. Уж сколько раз твердили миру, потоки не должны лезть в глобальные объекты.
1
32 / 21 / 4
Регистрация: 18.11.2012
Сообщений: 955
10.08.2019, 20:57  [ТС] 7
Цитата Сообщение от Renji Посмотреть сообщение
Уж сколько раз твердили миру, потоки не должны лезть в глобальные объекты.
Я ж написал, что код не мой, а я в параллельном программировании ничего не понимаю, надеюсь, что только пока, просто спросил для общей картины мира. Что нужно добавить или убрать чтобы процессор не так был загружен.

Я решил иначе, без многопоточности, а чтобы проверить свой ответ пошарил в интернете, где собственно и обнаружил вариант решения, который выложил здесь.

Мой вариант проще:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
string rand_string()
        {
            int len = randint_1(1, 100, randint(1,100));
            string s;
            for (int i = 0; i < len; ++i)
                s += char(randint_1(1, numeric_limits<char>::max()));
            return s;
        }
 
        cout << "\n\nvector<string>\n";
        vector<string> vs(max1);
    
 
        for (int i = 0; i < max1; ++i)
        {
            vs[i] = rand_string();
        }
 
        t1 = system_clock::now();
        sort(vs.begin(), vs.end());
        t2 = system_clock::now();
        cout << "Timw worker algorithm sort: " << duration_cast<milliseconds>(t2 - t1).count() << " ms.\n";
 
        vs.resize(max2);
        for (int i = 0; i < max2; ++i)
        {
            vs[i] = rand_string();
        }
 
        t1 = system_clock::now();
        sort(vs.begin(), vs.end());
        t2 = system_clock::now();
        cout << "Timw worker algorithm sort: " << duration_cast<milliseconds>(t2 - t1).count() << " ms.\n";


Добавлено через 2 минуты
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Здесь лучше сделать const size_t num_threads = std::thread::hardware_concurrency();
Первоначально так и было, только этот вариант закомментирован.
0
6738 / 4537 / 1839
Регистрация: 07.05.2019
Сообщений: 13,725
Записей в блоге: 1
10.08.2019, 21:28 8
Цитата Сообщение от Liss29 Посмотреть сообщение
Я ж написал, что код не мой, а я в параллельном программировании ничего не понимаю, надеюсь, что только пока, просто спросил для общей картины мира. Что нужно добавить или убрать чтобы процессор не так был загружен.
Процессор должен быть загружен полностью, при таких вычислениях. В этом и смысл.
И загружен он должен быть именно вычислениями, по большей части.
Когда же ты используешь в потоках динамическое выделение памяти - в классах std::sting, std::set - то практически всё время уходит на усыпление/запуск потоков при блокировке кучи.

Добавлено через 27 секунд
Цитата Сообщение от Liss29 Посмотреть сообщение
Первоначально так и было, только этот вариант закомментирован.
Раскомментарь.
0
32 / 21 / 4
Регистрация: 18.11.2012
Сообщений: 955
10.08.2019, 22:41  [ТС] 9
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Процессор должен быть загружен полностью, при таких вычислениях. В этом и смысл.
Ну, если так тогда пусть так и будет, пока я в эту тему не вникну. Дело в том, что я "наивно" предполагал, что распараллеливание это как раз, наоборот, разгружает процессор т.е. если мы вычисляем матрицы, например, то распределяя нагрузку на несколько ядер, можно, снизить нагрузку на ЦП, так я думал

Добавлено через 10 минут
P.S.
Вычисления проходят быстрее, если я не использую thread, разве так должно быть? Если задачу выполняет n-ядер и одно ядро разница должна быть, так? или я опять неправ.

Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Когда же ты используешь в потоках динамическое выделение памяти - в классах std::sting, std::set - то практически всё время уходит на усыпление/запуск потоков при блокировке кучи.
А если сразу выделить нужное количество памяти, допустим классу vector, тогда обращение к динамической памяти не будет или будет гораздо меньше, так усыпление/запуск либо не будет, либо будет гораздо меньше...
0
6738 / 4537 / 1839
Регистрация: 07.05.2019
Сообщений: 13,725
Записей в блоге: 1
11.08.2019, 01:55 10
Цитата Сообщение от Liss29 Посмотреть сообщение
Вычисления проходят быстрее, если я не использую thread, разве так должно быть? Если задачу выполняет n-ядер и одно ядро разница должна быть, так? или я опять неправ.
При правильно построенном алгоритме, должно быть ускорение в несколько раз, по сравнению с однопоточным. В n раз не получиться, потому что накладные расходы на запуск потоков и т.д. всё-таки присутствуют.
Пример можешь посмотреть вот здесь https://www.cyberforum.ru/blog... g5939.html

Цитата Сообщение от Liss29 Посмотреть сообщение
А если сразу выделить нужное количество памяти, допустим классу vector, тогда обращение к динамической памяти не будет или будет гораздо меньше, так усыпление/запуск либо не будет, либо будет гораздо меньше...
Ну да, если используешь массив вместо set и заранее зарезервируешь в нём место, будет намного лучше. Ещё там нужно убрать std::string и std:stringstream. Твою задачу можно решить вообще без выделения памяти в потоках.
thread_local static здесь тоже не нужно. Просто объяви default_random_engine ran; на стеке в функции потока и передай параметром.
0
32 / 21 / 4
Регистрация: 18.11.2012
Сообщений: 955
11.08.2019, 04:45  [ТС] 11
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
должно быть ускорение в несколько раз
без thread время работы (~80000 и ~800000) с thread (~82000 на 5000000 строк не дождался окончания).
Значит всё же в алгоритме, который привёл выше, присутствуют огромные недочёты, наверное такой же как я "специалист писал". Что же первый блин комом.

Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Просто объяви default_random_engine ran; на стеке в функции потока и передай параметром.
Я так делал:
C++
1
2
3
4
5
int randint(int min, int max, unsigned int seed = 0)
{
    static default_random_engine ran(seed);
    return uniform_int_distribution<>{min, max}(ran);
}
Зачем static убирать я не понял...

Цитата Сообщение от oleg-m1973 Посмотреть сообщение
в функции потока
как-то так...
C++
1
2
3
4
5
6
7
set<string> threaded_fill(int n)
{
    //...
    default_random_engine ran;
    //...
    vt.emplace_back(thread(fill_task, res(vs), n / num_threads, ref(mtx), ran));
}
0
2723 / 1887 / 559
Регистрация: 05.06.2014
Сообщений: 5,499
11.08.2019, 05:14 12
Цитата Сообщение от Liss29 Посмотреть сообщение
Зачем static убирать я не понял...
Затем, что static один на всех, то есть, тот самый глобальный объект, которым в тредах пользоваться по возможности не следует. Объект с модификатором thread_local один на весь тред, им пользоваться можно. Но в вашем случае достаточно объявить объект в fill_task, без всяких модификаторов и передавать в нужные функции по ссылке. Он тогда будет создан в стеке, а стек как раз один на тред изначально, без дополнительных прыжков с бубном.
2
6738 / 4537 / 1839
Регистрация: 07.05.2019
Сообщений: 13,725
Записей в блоге: 1
11.08.2019, 09:04 13
Лучший ответ Сообщение было отмечено Liss29 как решение

Решение

Цитата Сообщение от Liss29 Посмотреть сообщение
без thread время работы (~80000 и ~800000) с thread (~82000 на 5000000 строк не дождался окончания).
Значит всё же в алгоритме, который привёл выше, присутствуют огромные недочёты, наверное такой же как я "специалист писал". Что же первый блин комом.
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
static
void fill_task(std::array<char, 32> *p, size_t n)
{
    const int low = 0;
    const int high = 100000;
 
    std::default_random_engine ran;
    std::uniform_int_distribution<int> dis{low, high};
 
    for (size_t i = 0; i < n; ++i)
        _itoa(dis(ran), p[i].data(), 10);
}
 
int main()
{
    const size_t num_threads = std::thread::hardware_concurrency();
    const size_t n = 100'000'000;
 
    std::vector<std::array<char, 32>> data{n};
    std::vector<std::thread> threads;
    threads.reserve(num_threads);
    
    intmax_t dt1, dt2;
    std::cout << "par: ";
    {
        const auto tm = std::chrono::steady_clock::now();
        auto* p = data.data();
        size_t sz = data.size();
        for (size_t i = num_threads; i > 0; --i)
        {
            const size_t n2 = sz / i;
            threads.emplace_back(fill_task, p, n2);
 
            p += n2;
            sz -= n2;
        }
 
        for (auto &item: threads)
            item.join();
 
        const auto dt = std::chrono::steady_clock::now() - tm;
        dt1 = std::chrono::duration_cast<std::chrono::milliseconds>(dt).count();
        std::cout << dt1 << std::endl;
    }
 
    std::cout << "seq: ";
    {
        const auto tm = std::chrono::steady_clock::now();
        fill_task(data.data(), data.size());
        const auto dt = std::chrono::steady_clock::now() - tm;
        dt2 = std::chrono::duration_cast<std::chrono::milliseconds>(dt).count();
        std::cout << dt2 << std::endl;
    }
    std::cout << double(dt2) / dt1 << std::endl;
}
1
4 / 3 / 1
Регистрация: 08.08.2019
Сообщений: 238
11.08.2019, 16:33 14
oleg-m1973, а как вообще разделяются задачи между ядрами процессора при создании кастомных потоков? К примеру имеем мы два ядра и создаём еще(!) три потока, тоесть в сумме стало 4 и запускаем во всех потоках бесконечный цикл(только для примера, понимаю что так делать не желательно) и что в этом случае? Ядра 2 потока 4,разве одно ядро не может обрабатывать несколько потоков?
0
6738 / 4537 / 1839
Регистрация: 07.05.2019
Сообщений: 13,725
Записей в блоге: 1
11.08.2019, 16:40 15
Цитата Сообщение от Vanconts Посмотреть сообщение
Ядра 2 потока 4,разве одно ядро не может обрабатывать несколько потоков?
Может. Поток отрабатывает свой квант времени, 1мс вроде, потом шедулер его тормозит и запускает следующий поток и т.д.

Цитата Сообщение от Vanconts Посмотреть сообщение
и запускаем во всех потоках бесконечный цикл(только для примера, понимаю что так делать не желательно)
Вообще, в потоках обычно бесконечный цикл и работает, вернее цикл до события "стоп".
0
32 / 21 / 4
Регистрация: 18.11.2012
Сообщений: 955
11.08.2019, 16:41  [ТС] 16
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
_itoa(dis(ran), p[i].data(), 10);
Что такое dis() и где это находится вообще
0
4 / 3 / 1
Регистрация: 08.08.2019
Сообщений: 238
11.08.2019, 16:44 17
oleg-m1973, Тоесть поток сравним с функцией с бесконечным циклом только которая возвращает управление сразу?
0
6738 / 4537 / 1839
Регистрация: 07.05.2019
Сообщений: 13,725
Записей в блоге: 1
11.08.2019, 16:46 18
Цитата Сообщение от Liss29 Посмотреть сообщение
Что такое dis() и где это находится вообще
Строчкой выше объявлен std::uniform_int_distribution<int> dis{low, high};

Добавлено через 1 минуту
Цитата Сообщение от Vanconts Посмотреть сообщение
oleg-m1973, Тоесть поток сравним с функцией с бесконечным циклом только которая возвращает управление сразу?
Можно и так сказать. Цикл не обязательно бесконечный, можно просто выполнить задачу и завершиться.
1
4 / 3 / 1
Регистрация: 08.08.2019
Сообщений: 238
11.08.2019, 16:49 19
oleg-m1973, а как собственно процессор распределяет эти потоки между ядрами и могу ли я указать что я к примеру хочу чтобы какие-то два процесса выполнялись одним ядром?
0
6738 / 4537 / 1839
Регистрация: 07.05.2019
Сообщений: 13,725
Записей в блоге: 1
11.08.2019, 16:52 20
Цитата Сообщение от Vanconts Посмотреть сообщение
oleg-m1973, а как собственно процессор распределяет эти потоки между ядрами и могу ли я указать что я к примеру хочу чтобы какие-то два процесса выполнялись одним ядром?
Можешь. В винде - при помощи SetThreadAffinityMask можно указать на каких процессорах будет запускаться данный поток.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
11.08.2019, 16:52

Boost::thread vs std::thread
Доброго времени суток, решил углубить свои знания, и решил почитать про потоки, бустовые и те что в...

C++11. thread, mutex, thread-safety
есть функция, которая стартует 2 потока: для отлова нажатия клавиш и для выполнения действий void...

Thread
#include &lt;iostream&gt; #include &lt;thread&gt; using namespace std; void task1(){ cout &lt;&lt; &quot;task1&quot;; }...

Thread
Доброго времени суток господа! Вопрос в коде в комментариях. #include &lt;iostream&gt; #include...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.