С Новым годом! Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.67/3: Рейтинг темы: голосов - 3, средняя оценка - 4.67
0 / 0 / 0
Регистрация: 24.10.2017
Сообщений: 17

Параллельное программирование. Когда несколько потоков зависят от одного и один от нескольких

18.06.2020, 15:23. Показов 733. Ответов 15
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день!

Пытаюсь придумать решение такой задачи, в псевдокоде примерно так будет:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void base_foo()
{
   for (some_condition)
   {
      some_variable = some_serious_calculation();
      foo(some_variable);
   }
}
 
void foo(some_variable)
{
   for (int i=0; i < big_value; i++)
   {
      some_result[i]= variable;
   }
}
Хочу функцию foo распараллелить через std::thread. Сейчас я сделал эти функции членами одного класса, пременные some_variable и some_result соответсвенно закрытыми членами этого класса (чтоб в поток не предавать). Диапазон big_value я разбиваю на сегменты и в каждый поток передаю для обработки свой сегмент (например vector<T>::Iterator first и last).
Не могу врубиться как сделать чтоб все потоки foo ждали обновление переменной some_variable, а поток base_foo в свою очередь, ждал, когда потоки foo закончат вычисления с использованием текущего значения some_variable.

если не ошибаюсь, нужно копать в сторону std::condition_variable для ожидания всеми потоками одного. Как сделать обратное вообще не представляю.

Буду благодарен за подсказки.
Спасибо.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
18.06.2020, 15:23
Ответы с готовыми решениями:

Параллельное обновление нескольких ProgressBar из разных потоков
Доброго всем времени суток! Суть проблемы: На форме 2 прогресс бара и 2 кнопки Нажал 1 кнопку запустил 1 поток начал обновляться 1...

Параллельное программирование: при большом количестве задач и малом количестве потоков программа ломается
Есть функция вычисления интеграла, все исключения обработаны, эту функцию выполняют ThreadCount вспомогательных потоков, асинхронно,...

Один коннект и несколько потоков
Добрый день уважаемые форумчане. Хотелось бы прояснить следующий момент. Имеется одно глобальное подключение к базе данных и несколько...

15
 Аватар для Recrut_rf
381 / 324 / 65
Регистрация: 14.10.2014
Сообщений: 1,377
18.06.2020, 16:12
NTRNO, Вот два примера с условными переменными:

Кликните здесь для просмотра всего текста

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
#include <condition_variable>
#include <mutex>
#include <future>
#include <iostream>
 
bool readyFlag;
std::mutex readyMutex;
std::condition_variable readyCondVar;
 
void thread1()
{
    
    // подготовка данных для потока thread2
    std::cout << "<return>" << std::endl;
    std::cin.get();
 
    
    // сигнал, что поток thread1 выполнил условие
    {
        std::lock_guard<std::mutex> lg(readyMutex);
        readyFlag = true;
    } 
      // освобождаем блокировку
    readyCondVar.notify_one();
}
 
void thread2()
{
    
    // ожидаем, пока поток thread1 не перейдёт в соотояние готовность  
    {
        std::unique_lock<std::mutex> ul(readyMutex);
        readyCondVar.wait(ul, [] { return readyFlag; });
    } 
      // освобождаем блокировку
 
    
    // выполняем операции, которые должны быть выполнены до того, как
    // поток thread1 закончит подготовку
    std::cout << "done" << std::endl;
}
 
int main()
{
    auto f1 = std::async(std::launch::async, thread1);
    auto f2 = std::async(std::launch::async, thread2);
}


Кликните здесь для просмотра всего текста

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
#include <condition_variable>
#include <mutex>
#include <future>
#include <thread>
#include <iostream>
#include <queue>
 
 
/*
* в этом примере три потока заталкивают значения в очередь, которую читают и обрабатывают 
* два других потока  
*/
 
std::queue<int> queue;
std::mutex queueMutex;
std::condition_variable queueCondVar;
 
void provider(int val)
{
    
    // заталкиваем в очередь разные значения (от val до val + 5 с задержками, 
    // равными val миллисекунд)
    for (int i = 0; i < 6; ++i) {
        {
            std::lock_guard<std::mutex> lg(queueMutex);
            queue.push(val + i);
        } 
          // освобождаем блокировку
        queueCondVar.notify_one();
 
        std::this_thread::sleep_for(std::chrono::milliseconds(val));
    }
}
 
void consumer(int num)
{
    
    // выталкиваем из очереди значения, если они доступны
    // число num обозначает получателя 
    while (true) {
        int val;
        {
            std::unique_lock<std::mutex> ul(queueMutex);
            queueCondVar.wait(ul, [] { return !queue.empty(); });
            val = queue.front();
            queue.pop();
        } 
          // освобождаем блокировку
        std::cout << "consumer " << num << ": " << val << std::endl;
    }
}
 
int main()
{
    
    // запускаем три потока-поставщика значений 100+, 300+, 500+
    auto p1 = std::async(std::launch::async, provider, 100);
    auto p2 = std::async(std::launch::async, provider, 300);
    auto p3 = std::async(std::launch::async, provider, 500);
 
    
    // запускаем два потока-получателя, которые выводят значения
    auto c1 = std::async(std::launch::async, consumer, 1);
    auto c2 = std::async(std::launch::async, consumer, 2);
 
    system("pause");
 
    return 0;
}
1
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
18.06.2020, 16:20
Цитата Сообщение от NTRNO Посмотреть сообщение
Не могу врубиться как сделать чтоб все потоки foo ждали обновление переменной some_variable
можно воспользоваться std::shared_future
Цитата Сообщение от NTRNO Посмотреть сообщение
а поток base_foo в свою очередь, ждал, когда потоки foo закончат вычисления с использованием текущего значения some_variable.
можно с использованием нескольких(от каждого потока) std::future
1
 Аватар для Recrut_rf
381 / 324 / 65
Регистрация: 14.10.2014
Сообщений: 1,377
18.06.2020, 16:28
NTRNO, кстати да, как заметил zayats80888, к примеру, можно попробовать сделать с помощью разделяемых фьючерсов и функции async()

пример:

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
/*
Разделяемые фьючерсы
 
класс std::future позволяет обрабатывать будущий результат
параллельных вычислений. Однако этот результат можно обработать только один раз.
Второй вызов функции get() порождает непредвиденные последствия (в соответствии
с принципами стандартной библиотеки C++ желательно, но не обязательно,
чтобы реализации генерировали исключение std::future_error).
Однако иногда целесообразно несколько раз обрабатывать результат параллельных
вычислений, особенно если его обрабатывают несколько других потоков. Для этой цели
стандартная библиотека C++ предоставляет класс std::shared_future,
который допускает несколько вызовов функции get() и
возвращает один и тот же результат или генерирует одно и то же исключение.
 */
 
#include <future>
#include <thread>
#include <iostream>
#include <exception>
#include <stdexcept>
using namespace std;
 
int queryNumber()
{   
    // считываем число
    cout << "read number: ";
    int num;
    cin >> num;
    
    // генерируем исключение, если ничего не введено
    if (!cin) {
        throw runtime_error("no number read");
    }
 
    return num;
}
 
void doSomething(char c, shared_future<int> f)
{
    try {       
        // получаем количество символов, подлежащих выводу
        int num = f.get();  // get result of queryNumber()
 
        for (int i = 0; i < num; ++i) {
            this_thread::sleep_for(chrono::milliseconds(100));
            cout.put(c).flush();
        }
    }
    catch (const exception& e) {
        cerr << "EXCEPTION in thread " << this_thread::get_id()
            << ": " << e.what() << endl;
    }
}
 
int main()
{
    try {       
        // запускаем поток для запроса числа
        shared_future<int> f = async(queryNumber);
        
        // запускаем три потока, каждый из которых обрабатывает это число в цикле
        auto f1 = async(launch::async, doSomething, '.', f);
        auto f2 = async(launch::async, doSomething, '+', f);
        auto f3 = async(launch::async, doSomething, '*', f);
        
        // ждем завершения всех трех циклов
        f1.get();
        f2.get();
        f3.get();
    }
    catch (const exception& e) {
        cout << "\nEXCEPTION: " << e.what() << endl;
    }
    cout << "\ndone" << endl;
 
    system("pause");
}
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
18.06.2020, 17:00
Цитата Сообщение от NTRNO Посмотреть сообщение
Не могу врубиться как сделать чтоб все потоки foo ждали обновление переменной some_variable, а поток base_foo в свою очередь, ждал, когда потоки foo закончат вычисления с использованием текущего значения some_variable.
Напиши более конкретно, что ты пытаешься сделать, лучше сделай нормальный код с потоками, как ты его представляешь. Твой пример очень размытый, тут может быть масса решений.
Навскидку - здесь достаточно std::mutex, std::condition_variable и пары переменных.
0
0 / 0 / 0
Регистрация: 24.10.2017
Сообщений: 17
23.06.2020, 19:21  [ТС]
Спасибо, всем откликнувшимся
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Напиши более конкретно, что ты пытаешься сделать, лучше сделай нормальный код с потоками, как ты его представляешь. Твой пример очень размытый, тут может быть масса решений.
Хочу сделать следующее. У меня есть функция, которая в цикле (а цикл отсчитывает моменты времени) вычисляет некоторые свойства главного_объекта, а во вложенном цикле вычисляет свойства еще нескольких тысяч (миллионов) подчиненных_объектов которые зависят от свойств главного объекта.
Так вот, вычисление свойств главного_объекта хочу оствить в одном потоке, а вычисление свойств подчиненных объектов равномерно распределить по доступным потокам.

текущий код приводить смысла не вижу, упрощенно выглядит так

C++
1
2
3
4
5
6
7
8
for(auto i = 0; i < time; i++)
{
   base_object = some_function();
   for(auto j = 0; j < count_of_objects; j++)
   {
      other_objects[j] = other_function(base_object);
   }
}
вложенный цикл хочу раскидать на потоки, где каждый поток получает часть от общего числа объектов. Распределить по потокам проблем не составляет, как уже говорил, проблема взаимодействия между потоками. Прошу сильно тапками не кидать, в этих вопросах человек новый. Отдельное спасибо за приведенные примеры, разбираюсь.
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
23.06.2020, 19:38
Цитата Сообщение от NTRNO Посмотреть сообщение
вложенный цикл хочу раскидать на потоки, где каждый поток получает часть от общего числа объектов. Распределить по потокам проблем не составляет, как уже говорил, проблема взаимодействия между потоками.
Проблема взаимодействия - это чтобы внешний цикл ожидал завершение расчётов, которые сейчас делаются во вложенном цикле?
0
0 / 0 / 0
Регистрация: 24.10.2017
Сообщений: 17
24.06.2020, 16:03  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
это чтобы внешний цикл ожидал завершение расчётов, которые сейчас делаются во вложенном цикле?
Да, допустим у меня во вложенном цикле 1000 итерации, я их делю на 10 потоков: первому потоку от 0 до 99, второму 100-199 и тд. Поток в котором будет выполнятся внешний цикл, должен дождаться когда эти 10 потоков завершили свои итерации.
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
24.06.2020, 16:10
Цитата Сообщение от NTRNO Посмотреть сообщение
Да, допустим у меня во вложенном цикле 1000 итерации, я их делю на 10 потоков: первому потоку от 0 до 99, второму 100-199 и тд. Поток в котором будет выполнятся внешний цикл, должен дождаться когда эти 10 потоков завершили свои итерации.
Сделай через std::async. Что-то типа
C++
1
2
3
4
5
6
7
8
9
10
std::vector<std::future<other_object>> futs(count_of_objects);
for(auto i = 0; i < time; i++)
{
    base_object = some_function();
    for(auto j = 0; j < count_of_objects; j++)
        futs[j] = std::async(other_function, std::ref(base_object));
 
    for(auto j = 0; j < count_of_objects; j++)
        other_objects[j] = futs[j].wait();
}
0
0 / 0 / 0
Регистрация: 24.10.2017
Сообщений: 17
24.06.2020, 16:19  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Сделай через std::async. Что-то типа
Спасибо за подсказки! Тему пока не закрываю, мне потребуется некоторое время это "переварить", так как ранее не использовал.

Чуть позже отпишусь.
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
24.06.2020, 16:27
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Что-то типа
Имхо, вызывающий поток может взять на себя расчёты одного диапазона явно, что бы не простаивать впустую.

Добавлено через 2 минуты
Цитата Сообщение от NTRNO Посмотреть сообщение
я их делю на 10 потоков
Делить желательно на столько, сколько позволит распараллелить система.
0
0 / 0 / 0
Регистрация: 24.10.2017
Сообщений: 17
24.06.2020, 16:30  [ТС]
Цитата Сообщение от zayats80888 Посмотреть сообщение
вызывающий поток может взять на себя расчёты одного диапазона явно, что бы не простаивать впустую.
Да можно и так, но расчет во внешнем цикле тоже большой, и по времени соизмерим (смотрел тайминки в однопоточном вычислении). Получается, что 10 "младших" потоков будут ожидать первую итерацию главного цикла, потом они принимаются за работу, а пока считают, в главном цикле для них готовится новая порция ходных параметров. Я логику вижу такой. Возможно не самый лучший вариант

Добавлено через 1 минуту
Цитата Сообщение от zayats80888 Посмотреть сообщение
Делить желательно на столько, сколько позволит распараллелить система.
Это безусловно! Я так и делаю. Определяю количество возможных потоков минус один. В сообщении выше 10 для конкретики написал.
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
24.06.2020, 16:30
Цитата Сообщение от NTRNO Посмотреть сообщение
Да можно и так, но расчет во внешнем цикле тоже большой, и по времени соизмерим (смотрел тайминки в однопоточном вычислении). Получается, что 10 "младших" потоков будут ожидать первую итерацию главного цикла, потом они принимаются за работу, а пока считают, в главном цикле для них готовится новая порция ходных параметров. Я логику вижу такой. Возможно не самый лучший вариант
Имелось ввиду, что ты сначала запускаешь 9 потоков, а последний блок обрабатываешь сам. Только потом ждёшь завершения запущенных потоков
0
0 / 0 / 0
Регистрация: 24.10.2017
Сообщений: 17
24.06.2020, 16:32  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
сначала запускаешь 9 потоков, а последний блок обрабатываешь сам. Только потом ждёшь завершения запущенных потоков
А в чем плюс такого подхода?
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
24.06.2020, 17:02
Цитата Сообщение от NTRNO Посмотреть сообщение
в главном цикле для них готовится новая порция ходных параметров.
Так то же разумно, если подготовка занимает столько же примерно времени, сколько и расчет одной порции.
Главное забирай посчитанные данные(wait/get) только после того, как подготовишь новую порцию исходных.

---

NTRNO, а вообще, если помимо упомянутых задач есть и другие, то стоит задуматься о пуле потоков.
Только не о примитивном, а чутка навороченном, где помимо общей очереди задач, у каждого потока есть своя персональная, с более высоким приоритетом(а также возможность забирать задачи одним потоком у другого из его очереди).
Тогда, если данные для выполнения текущей задачи ещё не готовы, то её можно отложить в свою очередь и взять другую задачу(из своей, только ранее отложенную, общей, или очереди другого потока).
Правда придётся заморочиться, что бы это реализовать(хотя наверняка есть подобные готовые решения, не нужно велосипедить).
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
24.06.2020, 17:10
Цитата Сообщение от NTRNO Посмотреть сообщение
А в чем плюс такого подхода?
Экономишь на запуске одного потока
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
24.06.2020, 17:10
Помогаю со студенческими работами здесь

Один метод и несколько потоков
как работает один метод с несколькими потоками? будет ли это каждый раз запускаться один и тот же метод или каждый раз новый (как новый...

Работа с потоками. Несколько потоков обращаются в один
Есть один поток, он заполняет массив значениями. И есть неограниченное количество других потоков, которые забирают данные из этого массива....

Как сделать, чтобы когда пишешь с нескольких textbox в один label, инфа не удалялась бы, а добавлялась
private void textBox2_TextChanged(object sender, EventArgs e) { label.Text = textBox2.Text; } ...

Как сделать чтобы выводилось имя и долг человека в конце программы, когда должников не один а несколько?
Это задача в которой ввожу количество пользователей,имя и долг.У кого долг больше 30000 отправляется(ну как будто бы) уведомление.Как...

Параллельное выполнение потоков
Привет всем. Знаю что тема избитая уже, но что то не выходит все равно. Суть в том что в обработчике события есть 2 потока. Один поток...


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

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
Новые блоги и статьи
сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
Модель микоризы: классовый агентный подход 2
anaschu 06.01.2026
репозиторий https:/ / github. com/ shumilovas/ fungi ветка по-частям. коммит Create переделка под биомассу. txt вход sc, но sm считается внутри мицелия. кстати, обьем тоже должен там считаться. . . .
Расчёт токов в цепи постоянного тока
igorrr37 05.01.2026
/ * Дана цепь постоянного тока с сопротивлениями и источниками (напряжения, ЭДС и тока). Найти токи и напряжения во всех элементах. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа и. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru