Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.85/48: Рейтинг темы: голосов - 48, средняя оценка - 4.85
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80

Многопоточное программирование: как распараллелить цикл?

12.05.2019, 19:06. Показов 10061. Ответов 45

Студворк — интернет-сервис помощи студентам
Здравствуйте, господа. Возможно, я сейчас спрошу глупость, но тем не менее. В интернетах найти ответа не смог.

Допустим, у меня есть цикл do-while, в котором у меня выполняются операции - функции - с большуууууущим вектором,
таких функций несколько, причем эти функции принципиально последовательны, результат работы предыдущей функции
влияет на выполнение последующей. Но в пределах одной функции, которая работает с вектором поэлементно вполне
возможно реализовать параллельные вычисления для ускорения процесса. Вопрос! А как это сделать правильно?
В каждой функции есть range-based цикл, который перебирает элементы вектора.

Мне пришел в голову (тупой) вариант - просто распилить вектор, допустим, на 4 части и работать с каждой из частей,
получилось примерно следующее:

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
#include <vector>
#include "MyClass"
#include "f1"
#include "f2"
 
int main()
{
 
std::vector<MyClass> V_1;
std::vector<MyClass> V_2;
std::vector<MyClass> V_3;
std::vector<MyClass> V_4;
 
do
{
/********************************/
/* какая-то инициализация векторов */
/********************************/
 
//Работа с векторами 1 (функция f1 принимает вектор по ссылке 
//и работает с его элементами range-based циклом)
 
f1(V_1);
f1(V_2);
f1(V_3);
f1(V_4);
 
//Работа с векторами 2 (функция f2 принимает вектор по ссылке 
//и работает с его элементами range-based циклом)
 
f2(V_1);
f2(V_2);
f2(V_3);
f2(V_4);
 
}while (/*условие*/)
 
return 0;
}
Как я уже отмечал, параллельно выполнять операции f1 и f2 я не могу, так как f2 использует результат работы f1.
Но я могу засунуть выполнение, допустим, f1(V_1..4) в разные потоки и выполнять это дело параллельно.

Мой очень фиговый вариант:

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
#include <vector>
#include <thread>
#include "MyClass"
#include "f1"
#include "f2"
 
int main()
{
 
std::vector<MyClass> V_1;
std::vector<MyClass> V_2;
std::vector<MyClass> V_3;
std::vector<MyClass> V_4;
 
do
{
/********************************/
/* какая-то  инициализация векторов */
/********************************/
 
//Работа с векторами 1 (функция f1 принимает вектор по ссылке 
//и работает с его элементами range-based циклом)
 
std::thread t1([&]() { f1(V_1); });
std::thread t2([&]() { f1(V_2); });
std::thread t3([&]() { f1(V_3); });
f1(V_4);
 
t1.join();
t2.join();
t3.join();
 
//Работа с векторами 2 (функция f2 принимает вектор по ссылке 
//и работает с его элементами range-based циклом)
 
std::thread t4([&]() { f2(V_1); });
std::thread t5([&]() { f2(V_2); });
std::thread t6([&]() { f2(V_3); });
f2(V_4);
 
t4.join();
t5.join();
t6.join();
 
}while (/*условие*/)
 
return 0;
}
В принципе, такая краказябра даже работает. Но ее огромный недостаток - я в цикле постоянно открываю-закрываю кучу
потоков, и в результате вместо выигрыша в скорости от параллельных вычислений я получаю проигрыш в этой самой скорости
по сравнению даже со случаем тупого последовательного счета.

Я бы хотел (хотел бы я, ага...), чтобы у меня где-то в мэйне открывалось три потока, в которые я бы по мере необходимости
закидывал выполнение нужных функций. Но с библиотекой thread такого добиться не получается. Находил в интернетах
информацию, что можно использовать библиотеку pthread.h, но я так понял, что она типа устаревшая, я ее в моей VS-2017
даже подключить не могу.

Как быть?

Заранее благодарю за уделенное внимание)
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
12.05.2019, 19:06
Ответы с готовыми решениями:

Как распараллелить цикл while?
Доброго времени суток. При распараллелировании применять технологии Openmp. Было бы здорово, если бы ответ на вопрос, что в заглавии,...

Распараллелить цикл
Распараллелить цикл: For(i=2;i&lt;N;i++) For(j=2;i&lt;N;j++) A =A +A; #include &lt;iostream&gt; #include &lt;omp.h&gt; #include...

Многопоточное программирование
Добрый вечер, есть программа, которая ищет окно HWND Handle_ArcheAge; Handle_ArcheAge = FindWindow(L&quot;ArcheAge&quot;, NULL); //ищем...

45
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
13.05.2019, 22:06
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Юрий Мамонтов Посмотреть сообщение
Они принципиально вызываются для разных векторов
Да, вижу. Значит ошибка где-то в другом месте, которое не показано
0
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80
13.05.2019, 22:29  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Да, вижу. Значит ошибка где-то в другом месте, которое не показано
Вроде есть мысль на этот счет, завтра подумаю
0
 Аватар для Termit779
79 / 60 / 19
Регистрация: 22.11.2015
Сообщений: 193
14.05.2019, 04:42
Цитата Сообщение от Юрий Мамонтов Посмотреть сообщение
Вроде есть мысль на этот счет, завтра подумаю
Посмотрите в сторону флагов. У Вас состояние флагов задается в разных потоках. То есть цикл в потоке выполнился и пытается изменить флаг, и в другом потоке происходит тоже самое

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


Для флагов (в Вашем случаи) лучше использовать std::atomic_flag либо std::atomic<bool> либо std::atomic<int> http://www.cplusplus.com/refer... omic_flag/
0
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80
14.05.2019, 10:55  [ТС]
Цитата Сообщение от Termit779 Посмотреть сообщение
Посмотрите в сторону флагов. У Вас состояние флагов задается в разных потоках. То есть цикл в потоке выполнился и пытается изменить флаг, и в другом потоке происходит тоже самое
Точно, ошибка, моя невнимательность, спасибо большое) Но там ошибка еще в другом есть, аккуратнее надо все-таки в функции с вектором работать
0
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80
21.05.2019, 13:40  [ТС]
Господа, не знаю, есть ли кто живой в этой теме.

В первую очередь, большое спасибо всем за помощь и действительно дельные советы.

Написал я очередной вариант моего многопоточного расчета с учетом всех замечаний, сейчас это все выглядит следующим образом:

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <vector>
#include <mutex>
#include <condition_variable>
#include <thread>
#include "MyClass"
 
class Thread_function
{
public:
    enum thread_flags
    {
        STOP,
        F1,
        F2
    };
 
    void Calc_processing(std::vector<MyClass> &My_Vect_1_procs) noexcept;
 
    std::condition_variable Ready_to_Calculate;
    std::mutex Ready_to_Calculate_mutex;
 
    std::condition_variable Ready_to_Continue;
    std::mutex Ready_to_Continue_mutex;
 
    Thread_function()
    {
        this->flag = STOP;
    }
 
    Thread_function(const Thread_function &Other)
    {
        this->flag = Other.flag;
    }
 
    Thread_function(const Thread_function::thread_flags flag)
    {
        this->flag = flag;
    }
 
    void Set_flag(const Thread_function::thread_flags flag)
    {
        this->flag = flag;
    }
 
        //Типа предикаты
    bool Check_F2()
    {
        return (this->flag == F2);
    }
 
    bool Check_F1()
    {
        return (this->flag == F1);
    }
 
    bool Check_STOP()
    {
        return (this->flag == STOP);
    }
 
private:
    volatile thread_flags flag;
}
 
void Thread_function::Сalc_processing(std::vector<MyClass> &My_Vect_1_procs) noexcept
{
    while (true)
    {
        {
            std::unique_lock<std::mutex> lock(this->Ready_to_Calculate_mutex);
            this->Ready_to_Calculate.wait(lock, [&]() { return this->Check_F1(); });
        }
 
        f1(My_Vect_1_procs);
 
        this->Set_flag(STOP);
        this->Ready_to_Continue.notify_one();
        
        {
            std::unique_lock<std::mutex> Lock(this->Ready_to_Calculate_mutex);
            this->Ready_to_Calculate.wait(Lock, [&]() { return this->Check_F2(); });
        }
 
        f2(My_Vect_1_procs);
 
        this->Set_flag(STOP);
        this->Ready_to_Continue.notify_one();
    }
}
 
int main()
{
       const size_t procs = std::thread::hardware_concurrency(); //Максимально возможное число одновременно
                                              //выполняемых потоков
    std::vector<std::vector<MyClass>> My_Vect_1_procs(procs); //Мои объекты для расчетов
 
        std::vector<Thread_function> Els; //Вектор с объектами типа Thread_function
 
    for (size_t i = 0; i < procs - 1; ++i)
    {
        Els.emplace_back(Thread_function::thread_flags::STOP); //Количество элементов типа Thread_function
    }                                                                                          //совпадает с числом потоков
 
    std::vector<std::thread> threads; //Вектор потоков
    for (size_t i = 0; i < procs - 1; ++i)
    {
        threads.emplace_back([&]() //Открываем потоки, засовывая в них метод класса
        {                                        //Thread_function, который берется у каждого объекта данного класса
                                                          //Захват контекста по ссылке, сама функция Calc_processing принимает вектор по ссылке
                                                          //Причем каждому вызову сообщается свой вектор (один веткор резервный, с ним работает мэйн-поток)
            Els[i].Calc_processing(My_Vect_1_procs[i]);
        });
    }
 
 
        do
        {
                for (size_t i = 0; i < procs - 1; ++i) //Сообщаю каждому потоку начать считать первую функцию
        {
            Els[i].Set_flag(Thread_function::thread_flags::F1);
            Els[i].Ready_to_Calculate.notify_one();
        }
        
                f1(My_Vect_1_procs[procs - 1]);
        
        for (size_t i = 0; i < procs - 1; ++i) //Жду, пока все потоки не отчитаются  о выполнении
        {
            {
                std::unique_lock<std::mutex> Lock(Els[i].Ready_to_Continue_mutex);
                Els[i].Ready_to_Continue.wait(Lock, [&]()
                { return (Els[i].Check_STOP()); });
            }
        }
 
                /******************КАКИЕ ТО ДЕЙСТВИЯ****************/
 
                for (size_t i = 0; i < procs - 1; ++i) //Сообщаю каждому потоку начать считать вторую функцию
        {
            Els[i].Set_flag(Thread_function::thread_flags::F2);
            Els[i].Ready_to_Calculate.notify_one();
        }
        
                f2(My_Vect_1_procs[procs - 1]);
        
        for (size_t i = 0; i < procs - 1; ++i) //Жду, пока все потоки не отчитаются  о выполнении
        {
            {
                std::unique_lock<std::mutex> Lock(Els[i].Ready_to_Continue_mutex);
                Els[i].Ready_to_Continue.wait(Lock, [&]()
                { return (Els[i].Check_STOP()); });
            }
        }
 
                 /******************КАКИЕ ТО ДЕЙСТВИЯ****************/
 
         } while (/*УСЛОВИЕ*/);
 
         return 0;
}
По логике вроде бы все должно работать: функция ждет в точках wait пинка от своей условной переменной, а также
нужного флага, после чего считает (вызывает мою функцию), отправляет сигнал, что она посчитала, ставит флаг СТОП,
а затем снова ждет.

В мэйне в цикле я в нужный момент времени выставляю флаги, а затем пинаю условные переменные (она у каждого
экземпляра класса Els, который и содержит метод Calc_processing, своя, как и мьютекс, значит у каждого потока своя
пара переменная-мьютекс). После этого я в мэйне тоже запускаю вычисления с оставшимся вектором типа MyClass
(специально создаю вектор векторов размером с число потоков, а потоков открываю на один меньше - один из векторов
будет обрабатываться в основном потоке). После того, как в мэйне все посчиталось, опрашиваю каждый поток на предмет
окончания вычислений с помощью wait (там уже задействована другая пара условная переменная - мьютекс) + флаг STOP.
После все повторяется с другой функцией.

Я вижу работу моего кода так. Правда, Студия видит это по-другому. Почему-то в какие-то моменты времени прога виснет
или ругается на out-of-range вектора. Все перепроверил, вектора по длинам вроде бы все согласованы. Зависает прога,
судя по всему, во время ожидания в теле функции Calc_Processing, при этом мне удалось поймать ошибку, как на скриншоте.

Господа, с чем это может быть связано? Какие мысли?

Благодарю за уделенное внимание)
Миниатюры
Многопоточное программирование: как распараллелить цикл?  
0
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80
21.05.2019, 13:48  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Юрий Мамонтов, вот сделал что-то похожее. Вычисляет что-то в массиве m_data по блокам, кол-во потоков = кол-во процессоров
Спасибо, учел в предложенной мной выше реализации, но все равно что-то я не так делаю, программа виснет
(подробности - выше)
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.05.2019, 15:08
Цитата Сообщение от Юрий Мамонтов Посмотреть сообщение
Els[i].Set_flag(Thread_function::thread_flags: :F1);
* * * * * * Els[i].Ready_to_Calculate.notify_one();
notify всегда нужно делать под мьютексом, под тем же, под которым ты делаешь wait(Lock)

C++
1
2
3
std::lock_guard<std::mutex> Lock(Els[i].Ready_to_Continue_mutex);
            Els[i].Set_flag(Thread_function::thread_flags::F2);
            Els[i].Ready_to_Calculate.notify_one();
Добавлено через 2 минуты
В Thread_function::Сalc_processing то же самое

Добавлено через 2 минуты
У тебя должен быть один мьютекс и одна condition_variable, общие для всех потоков, посмотри мой код
1
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80
21.05.2019, 16:53  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
У тебя должен быть один мьютекс и одна condition_variable, общие для всех потоков, посмотри мой код
oleg-m1973, Я видел, но почему нельзя использовать кучу мьютексов?

Добавлено через 2 минуты
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
У тебя должен быть один мьютекс и одна condition_variable, общие для всех потоков, посмотри мой код
Не получится ситуация, когда все потоки сидят на одной condition_variable, и если один из потоков кинет notifiy, это приведет
к срабатыванию функции wait() в ожидающем потоке?
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.05.2019, 17:24
Цитата Сообщение от Юрий Мамонтов Посмотреть сообщение
Я видел, но почему нельзя использовать кучу мьютексов?
А ну да, вижу. В твоём случае можно. Сделай только блокировку для notify_one/all и должно вроде заработать.

Добавлено через 18 минут
Цитата Сообщение от Юрий Мамонтов Посмотреть сообщение
Не получится ситуация, когда все потоки сидят на одной condition_variable, и если один из потоков кинет notifiy, это приведет
к срабатыванию функции wait() в ожидающем потоке?
Получится. Он проверит своё условие и снова заснёт.

Добавлено через 3 минуты
В моём случае, он проверит есть ли ещё необработанные данные, если есть - обработает. Т.е. блоков массива (или массивов) может быть сколько угодно, я гарантирую, что все они будут обработаны

Добавлено через 2 минуты
В твоём случае - ты последовательно запускаешь все потоки. Соответственно может получится, что первый уже всё сделал и не знает чем заняться, а последний ещё даже не запустился.
0
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80
21.05.2019, 17:33  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
В твоём случае - ты последовательно запускаешь все потоки. Соответственно может получится, что первый уже всё сделал и не знает чем заняться, а последний ещё даже не запустился.
Действительно, согласен, надо по-другому сделать..
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
notify всегда нужно делать под мьютексом, под тем же, под которым ты делаешь wait(Lock)
C++Выделить код
1
2
3
std::lock_guard<std::mutex> Lock(Els[i].Ready_to_Continue_mutex);
* * * * * * Els[i].Set_flag(Thread_function::thread_flags: :F2);
* * * * * * Els[i].Ready_to_Calculate.notify_one();
Добавлено через 2 минуты
В Thread_function::Сalc_processing то же самое

Работает! Спасибо огромное за совет!

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

Добавлено через 2 минуты
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
В моём случае, он проверит есть ли ещё необработанные данные, если есть - обработает. Т.е. блоков массива (или массивов) может быть сколько угодно, я гарантирую, что все они будут обработаны
Можно в двух словах, что Ваш код в принципе делает?) В многопоточном режиме не получается адекватно отследить логику выполнения.. С тем, какие команды потокам раздаются, я вроде разобрался

Ну или я глупенький..
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.05.2019, 17:48
Цитата Сообщение от Юрий Мамонтов Посмотреть сообщение
Можно в двух словах, что Ваш код в принципе делает?) В многопоточном режиме не получается адекватно отследить логику выполнения.. С тем, какие команды потокам раздаются, я вроде разобрался
Потоки ThreadProc:
Под блокировкой ждут события, что появились данные для обработки - if (m_i < m_data.size()) или останока - stop == true
Вычисляют какой блок им обрабатывать i = m_i; n = m_i = std::min(m_data.size(), m_i + m_block_size);
Выставляют признак что заняты - ++m_inuse;

Снимают блокировку
Обрабатывают данные
Под блокировкой снимают признак, что заняты --m_inuse;

Добавлено через 5 минут
Поток main() в цикле:
Под блокировкой заполняет массив
Выставляет признак, что есть данные data.m_i = 0;
Сообщает потоком, что можно работать data.m_cv.notify_all();
Снимает блокировку

Ждёт, пока нет больше данных для обработки data.m_i < data.m_data.size() и все потоки завершили обработку данных data.m_inuse != 0

После выхода из цикла:
под блокировкой выставляет data.m_stop = true; и сообщает об этом потокам

Снимает блокировку и ждёт пока потоки завершатся thread.join();
1
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80
23.05.2019, 14:34  [ТС]
oleg-m1973, я вроде разобрался, спасибо огромное за помощь. Во многом воспользовался
Вашим кодом, но немного переделал его "под себя". Все шуршит, все работает, НО... Опять возникла проблемка)
Сейчас мой код выглядит так:

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include <vector>
#include <mutex>
#include <condition_variable>
#include <thread>
#include "MyClass"
#include "Mesh"
 
class Thread_function
{
public:
    enum thread_flags
    {
        STOP,
        F1,
        F2
    };
 
    void Calc_processing(std::vector<MyClass> &My_Vect_1_procs, Mesh * mesh, int &Contr_Sum) noexcept;
 
    std::condition_variable Ready_to_Continue;
    std::mutex Ready_to_Continue_mutex;
 
    Thread_function(const int Quantity)
    {
    for (int i = 0; i < Quantity; ++i) 
    {
        this->MyVectors.emplace_back();
    }
    }
 
    std::vector<std::vector<MyClass>> MyVectors; //Data - вектор векторов
 
    volatile bool stop_flag = false;
 
    volatile int threads_in_use = 0;
 
    volatile Thread_function::FLAGS Task_flag = Thread_function::FLAGS::STOP;
 
}
 
void Thread_function::Сalc_processing(std::vector<MyClass> &My_Vect_1_procs, Mesh * mesh, int &Contr_Sum) noexcept
{
    while (true)
    {
    while (true)
    {
        {
            std::unique_lock<std::mutex> lock(this->Ready_to_Continue_mutex);
            if (this->stop_flag)
            {
                return;
            }
            if (this->Task_flag == Thread_function::FLAGS::DISTR)
            {
                ++this->threads_in_use;
                break;
            }
            this->Ready_to_Continue.wait(lock);
        }
    }
 
    f1(My_Vect_1_procs, mesh, Contr_Sum);
 
    {
        std::unique_lock<std::mutex> Lock(this->Ready_to_Continue_mutex);
        --this->threads_in_use;
        if (this->threads_in_use == 0)
        {
            this->Task_flag = Thread_function::FLAGS::STOP;
        }
        this->Ready_to_Continue.notify_all();
    }
        
    while (true)
    {
        {
            std::unique_lock<std::mutex> lock(this->Ready_to_Continue_mutex);
            if (this->stop_flag)
            {
                return;
            }
            if (this->Task_flag == Thread_function::FLAGS::MOOVE)
            {
                    ++this->threads_in_use;
                break;
            }
            this->Ready_to_Continue.wait(lock);
        }
    }
 
    f2(My_Vect_1_procs, mesh, Contr_Sum);
 
    {
        std::unique_lock<std::mutex> Lock(this->Ready_to_Continue_mutex);
        --this->threads_in_use;
        if (this->threads_in_use == 0)
        {
            this->Task_flag = Thread_function::FLAGS::STOP;
        }
        this->Ready_to_Continue.notify_all();
    }
        
    }
}
 
int main()
{
int Size = 10000;
   Mesh *mesh = new Mesh[Size];
   int Contr_Sum = 0;
   const size_t procs = std::thread::hardware_concurrency(); 
   MyClass MyVect(procs);
 
    std::vector<std::thread> Threads; //Вектор потоков
    for (size_t i = 0; i < procs - 1; ++i)
    {
    Threads.emplace_back([&]()
    {
        MyVect.Cals_processing(MyVect.MyVectors[i], mesh, Contr_Sum);
    });
        }
 
   
    do
    {
       {
       std::unique_lock<std::mutex> Lock(Els.Ready_to_Continue_mutex);
           Els.Task_flag = Thread_function::FLAGS::F1;
       Els.Ready_to_Continue.notify_all();
    }
        
        f1(MyVect.MeVectors[procs - 1], mesh, Contr_Sum);
        
        {
       std::unique_lock<std::mutex> Lock(Els.Ready_to_Continue_mutex);
       while (Els.Task_flag != Thread_function::FLAGS::STOP || Els.threads_in_use != 0)
       {
        Els.Ready_to_Continue.wait(Lock);
       }
    }
 
        /******************КАКИЕ ТО ДЕЙСТВИЯ****************/
 
        {
        std::unique_lock<std::mutex> Lock(Els.Ready_to_Continue_mutex);
        Els.Task_flag = Thread_function::FLAGS::F2;
        Els.Ready_to_Continue.notify_all();
    }
        
        f2(MyVect.MyVectors[procs - 1], mesh, Contr_Sum);
        
        {
        std::unique_lock<std::mutex> Lock(Els.Ready_to_Continue_mutex);
        while (Els.Task_flag != Thread_function::FLAGS::STOP || Els.threads_in_use != 0)
        {
        Els.Ready_to_Continue.wait(Lock);
        }
    }
 
       /******************КАКИЕ ТО ДЕЙСТВИЯ****************/
 
        } while (/*УСЛОВИЕ*/);
 
    {
    std::unique_lock<std::mutex> Lock(Els.Ready_to_Continue_mutex);
    Els.stop_flag = true;
    Els.Ready_to_Continue.notify_all();
    }
    
   for (auto &thread : Threads)
   {
    if (thread.joinable())
    {
        thread.join();
    }
   }
   Threads.clear();
 
   return 0;
}
Я здесь добавил еще один объект - массив элементов типа Mesh, это сетка для моих физических вычислений, каждый
элемент - это ячейка сетки.Структура этого класса не так важна. Допустим, в нем есть нестатическое поле int Value,
которое хранит число обращений к данной ячейке сетки со стороны векторов MyVect.MyVectors_1...procs, причем КАЖДЫЙ
ЭЛЕМЕНТ ВЕКТОРА МОЖЕТ ОБРАТИТЬСЯ К СЕТКЕ НЕ БОЛЕЕ И НЕ МЕНЕЕ ОДНОГО РАЗА, таким образом полное число
обращений к сетке (то есть ко всем элементам массива mesh[Size], если просуммировать их поля Value) должно в точности
совпадать с СУММАРНЫМ числом элементов во ВСЕХ векторах MyVect.MyVectors_1...procs.

Переменная Contr_Sum также отслеживает число "учтенных", "задействованных" элементов векторов MyVect.MyVectors_1...procs.

По моей логике, сумма полей Value массива mesh[Size] должна совпадать с контрольной суммой Contr_Sum и с полным числом
всех элементов всех векторов. Но у меня все эти три числа РАЗНЫЕ! Количество элементов в векторах самое большое, сумма
сумма значений полей Value массива mesh[Size] поменьше (иногда существенно, иногда не очень), а Contr_Sum сильно меньше.

Таким образом, я делаю вывод, что мои функции f1 и f2 обрабатывают не все элементы векторов. Как бы не так!

Сами функции выглядят примерно так:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void f1..2(std::vector<MyClass> &V_1..procs, Mesh * mesh, int &Contr_Sum)
{
int Count = 0;
std::mutex m_f;
int p; //Номер ячейки сетки, с которым работаем
   for (auto &i : V_1..procs)
   {
       /***ОПРЕДЕЛЕНИЕ ОБРАЩЕНИЯ К КОНКРЕТНОЙ ЯЧЕЙКЕ МАССИВА mesh***/
       p = /* Number */;//Вычислили номер ячейки сетки
      //Какие-то действия
       {
    std::lock_guard<std::mutex> Lock_f(m_f);
    ++mesh[p].Value;
        ++Contr_Sum;
        ++Count;
       }
   }
}
Переменная Count сравнивается с размером текущего вектора std::vector<MyClass> V_1..procs, и здесь никаких отклонений
не выявлено: всегда четко (Count == V_1..procs.size()) - true. Но по завершению расчетов всеми потоками оказывается,
что Contr_Sum сильно отличается от Суммы размеров всех векторов V_1..procs, как и сумма всех полей Value сетки mesh.
Причем и между собой сумма (mesh[i].value) и Contr_Sum не равны!

Значит, я не могу утверждать, что после отработки всех потоков у меня запишутся ВСЕ необходимые данные.

ХОТЯ! в пределах КАЖДОГО потока счетчик итераций Count неизменно совпадает с размерами передаваемого вектора.

Тогда почему так криво инкрементируются внешние счетчики???

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

Добавлено через 40 минут
oleg-m1973, в остальном - все отлично работает) спасибо за помощь)
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
23.05.2019, 15:18
Цитата Сообщение от Юрий Мамонтов Посмотреть сообщение
По моей логике, сумма полей Value массива mesh[Size] должна совпадать с контрольной суммой Contr_Sum и с полным числом
всех элементов всех векторов. Но у меня все эти три числа РАЗНЫЕ! К
Наверное потому, что она должна быть std::atomic<int> Contr_Sum

Добавлено через 33 секунды
Цитата Сообщение от Юрий Мамонтов Посмотреть сообщение
oleg-m1973, в остальном - все отлично работает) спасибо за помощь)
А время замерял, по сравнению с одним потоком?
0
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80
23.05.2019, 15:21  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
А время замерял, по сравнению с одним потоком?
Строго - нет, не измерял. На глаз - существенное увеличение скорости расчета, в разы

Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Наверное потому, что она должна быть std::atomic<int> Contr_Sum
Надо попробовать. Но тогда во избежание казусов надо все поля моей сетки mesh делать атомарными???
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
23.05.2019, 15:23
Цитата Сообщение от Юрий Мамонтов Посмотреть сообщение
Надо попробовать. Но тогда во избежание казусов надо все поля моей сетки mesh делать атомарными???
Думаю, лучше не надо std::atomic это нифига не бесплатная штука.
0
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80
23.05.2019, 15:25  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Наверное потому, что она должна быть std::atomic<int> Contr_Sum
Просто следствие неправильного счета элементов - это неучтение всех данных, какие-то числа просто не записываются в сетку

Что плохо
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
23.05.2019, 15:25
У тебя разные потоки обращаются к одно ячейке mesh?
0
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80
23.05.2019, 15:27  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Думаю, лучше не надо std::atomic это нифига не бесплатная штука.
А как тогда убедиться, что мои данные были записаны в mesh?

Пока что я убедился, скорее, в обратном)

Добавлено через 1 минуту
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
У тебя разные потоки обращаются к одно ячейке mesh?
Не всегда, но в общем случае можно считать, что это так
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
23.05.2019, 15:28
Цитата Сообщение от Юрий Мамонтов Посмотреть сообщение
Думаю, лучше не надо std::atomic это нифига не бесплатная штука.
А как тогда убедиться, что мои данные были записаны в mesh?
Покажи, хотя бы, как ты их пишешь
0
6 / 6 / 0
Регистрация: 27.11.2016
Сообщений: 80
23.05.2019, 15:34  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
У тебя разные потоки обращаются к одно ячейке mesh?
Я это не контролирую, обращение к ячейкам можно считать случайным

Добавлено через 4 минуты
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Покажи, хотя бы, как ты их пишешь
Пока атомарными не пишу, в данный момент класс mesh представляет собой буквально следующее:

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#pragma once
 
#include <cmath>
#include <fstream>
#include <iostream>
#include <conio.h>
#include <sstream>
#include "Get_Velocity.h"
#include "Global_Variables.h"
//#include "Emission_data.h"
 
#define debug_mesh
 
class Spher_1D_Area //Описывает точку сферической области
{
    friend class Spher_Electron;
    friend class Thread_function;
public:
    //static long int Count; //Число точек
    static int Count_max; //Максимальное число точек
    static double Rin; //Радиус внутренней сферы
    static double Rout; //Радиус внешней сферы
    static double h1; //Первый пространственный шаг
    static double k; //Множитель для геометрической прогрессии
    static double Degr_value;
 
    static double Pulse_Duration; //Длительность импульса высокого напряжения
    static double Pulse_Front; //Длительность фронта напряжения
 
    static int Els_count(Spher_1D_Area *Mesh);
 
    Spher_1D_Area(); //Конструктор по умолчанию
    ~Spher_1D_Area(); //Деструктор
 
    static double W_rel;
 
    //Получить индекс точки
    //long int GetID();
 
    //Получить номер ячейки
    static int GetNum(double R);
 
    //Ввести условия моделирования
    friend void Input_Spher_Mesh_Params(Spher_1D_Area buf);
    friend void Input_Spher_Dirihle_Board_Conds(double& left_dir_board_phi, double& right_dir_board_phi, Spher_1D_Area buf);
    //Решить уравнение Пуассона
    friend void Spher_1D_Dirihle_Poisson_Progon(Spher_1D_Area * Mesh);
 
    //Заполнить сетку значениями координат и сеточных коэффициентов
    static void Build_Dirihle_Spher_1D_Mesh(Spher_1D_Area * Mesh);
 
    //Установить ГУ
    static void Set_Dirihle_Board_Conditions(Spher_1D_Area * Mesh, double left, double right, double cur_time);
 
    //Установить множитель для шага сетки, который увеличивается в геометрической прогрессии
    static void SetK();
    
    //Получить катодный потенциал
    static double GetCathodePotential(Spher_1D_Area* Mesh);
 
    //Получить напряженность поля у катода
    static double GetCathodeE_intensity(Spher_1D_Area* Mesh);
 
    //Объем шарового слоя
    static double Spher_Layer_Volume(double Rmax, double Rmin);
 
    //Обнулить заряд на сетке для того, чтобы раздать его заново
    static void ResetChargeDensity(Spher_1D_Area* Mesh);
 
    //Autosave
    static void AutoSave(Spher_1D_Area* Mesh, double TotalTime, double Jo, double Jo_prev);
 
    //Раздача заряда и интерполяция силы
    static double S_func(Spher_1D_Area* Mesh, int p, double ri, double rp);
 
    //Раздача заряда в стационарном приближении сплошной среды
    static void ContinMediumChargeDistr(Spher_1D_Area* Mesh, double J);
 
protected:
    volatile int Quantity;
    //Координата точки
    double Ri;
 
    //Шаг ПОСЛЕ ТОЧКИ
    //double hi;
 
    //Сеточные коэффициенты
    double a;
    double b;
    double c;
 
    //Значение потенциала в данной точке
    double Phi;
 
    //Напряженность поля в точке
    double Ei;
 
    //Значение заряда в данной точке пространства
    double Ro_prev;
    double Ro; /*ВОТ СЮДА МНЕ И НАДО ЗАПИСАТЬ РЕЗУЛЬТАТ РАСЧЕТА ФУНКЦИИ  f1*/
    double DRo;
 
    //Объем ячейки
    double Vp;
 
    //Шаг ячейки (приведенный)
    double hi_Degr;
 
    //long int ID;
 
    //Установить шаг после точки
    //void Set_hi(Spher_1D_Area& Point);
 
    //Коэффициенты для метода прогонки
    double Alpha_prog; 
    double Beta_prog;
 
    static double Get_F_from_k(double K_contr);
    static void E_calculation(Spher_1D_Area * Mesh);
};
Добавлено через 41 секунду
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Покажи, хотя бы, как ты их пишешь
Сейчас сделаю их так, как я их вижу в "атомарном виде"
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
23.05.2019, 15:34
Помогаю со студенческими работами здесь

многопоточное программирование на с++
необходимо создать приложение которое с использованием thread (потоков) решит уравнение ну например дроюбное , то есть один поток решит...

Многопоточное программирование
Добрый день! Можете посоветовать кнгиги по многопоточному программированию на C++, не привязанные к ОС, использую Qt. Желательно, что...

Многопоточное программирование
Как я понимаю, есть много библиотек, с помощью которых мы можем использовать многопоточность. Чем они отличаются? Какая лучше? Почему? ...

Многопоточное программирование
Доброго времени суток! Пытался разобраться но не нашел понятных ответов на некоторые вопросы, поэтому, пожалуйста, не отсылайте к...

Не получается распараллелить цикл
Добрый день! В ходе опытов я выяснил, что распараллеливание цикла for с помощью OMP не приносит выигрыша во времени. Т.е. код...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip Сканируйте QR-код на мобильном и вы увидите, что появится джойстик для управления главным героем. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru