49 / 147 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
1

Ресурсы из 2х потоков использовать несколькими потребителями

16.04.2021, 20:55. Показов 1274. Ответов 11

можно ли так отправить людей в отпуск - задача из этого поста
Сценарий: отдых в горах
Процесс А и процесс Б решили провести отпуск в горах. Для этого им необходима машина и лыжи(ресурсы). Есть возможность отдолжить только одни лыжи и одну машину. Если процесс получит машину и лыжи, то отправится в отпуск, а другой процесс будет ждать его возвращения, чтобы получить лыжи и машину. Если же один процесс получит лыжи, а другой машину, то оба будут ждать недостающего снаряжения и никто(никогда) не поедет в отпуск. Возникает взаимоблокировка(deadlock), когда каждый процесс ждет освобождения ресурса используемого другим процессом.
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
// Т.е. флаг завершения нужно ставить внутри цикла producer или проверять его в ожидании на condition_variable.
  
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
#include <string>
#include <atomic>
 
#include <condition_variable>
 
using namespace std;
 
volatile atomic_int cn=0;   // qty - times used equipment
volatile int cntAll=0;  // all people for vacations // условие окончания очереди
volatile atomic_int value = 0;  // equipment (=2 per one vacation)
mutex m, mReport;       // 1 мьютекс для equipment... 1 для cout
condition_variable cv_var1, cv_var2;    // 2 C.V. (car & skis)
bool condSkis, condCar;                 // 2 условия
 
void Report( int rep, string u=""){
    std::lock_guard<mutex> lock(mReport);       
    switch (rep) {
        case(0):    cout <<"skis=free " << endl; break;
        case(1):    cout <<"skis in use " << endl; break;
        case(2):    cout <<"car=free " << endl; break;
        case(3):    cout <<"car in use " << endl; break;
        case(4):    cout << "\n>>>> " << u << " need equipment " << endl; break;
        case(5):    cout << u << " goes on vacation " << endl; break;
        case(6):    cout << u << " returned home " << endl; break;
    }
}
 
// thread (dF)
void getSkis() {
    unique_lock<mutex> lock1(m);
    while (true) {
        if (cn==cntAll) return;
        
        Report(0);
        // unlocking while wait...
        //while( !condSkis )
            cv_var1.wait( lock1, [&]{return condSkis; } );
        
        Report(1);          
        value +=1;    
        //cv_var1.notify_one(); // ????????? надо того же...
    }
  return;
}
 
// thread (dO)
void getCar() {
    unique_lock<mutex> lock1(m);
    while (true) {
        if (cn==cntAll) return;
        
        Report(2);
        // unlocking while wait...
        //while( !condCar )
            cv_var2.wait( lock1, [&]{return condCar; } );
 
        Report(3);
        value +=1;      
        //cv_var2.notify_one(); // ????????? надо того же... => надо чтобы соотв поток ждал
    }
  return;
}
 
// thread (CALC)
void setUser(string nm) {   
    unique_lock<mutex> lock1(m);
    while (true) {      
        if (value==0) {
            Report(4, nm);
            
            condCar=false; condSkis=false;  // needed
            Report(1); Report(3);   // ВЗЯЛИ ЛЫЖИ И МАШИНУ
            cv_var1.notify_all(); 
            cv_var2.notify_all();
    
            Report(5,nm);   // // user go on vacation...
            
            //std::this_thread::sleep_for(std::chrono::seconds(3));     // лучше не надо -Scheduler может сработать непонятно
            
            Report(6,nm);   // user returned home...            
                
            value=0; // equipment
            condCar=true; condSkis=true;    // freed
            Report(0); Report(2);   // ВЕРНУЛИ ЛЫЖИ И МАШИНУ
            cn+=1;  // окончание обслуживания клиента (bool doneAll для всех потоков при cn==cntAll)
            cv_var1.notify_all(); 
            cv_var2.notify_all();
            
            break;
        }
    }
 
    return;
}
 
 
int main()  
{
  bool condSkis=true;   // в наличии
  bool condCar=true;    // в наличии
 
  vector<string> user{ "John","Paul", "Steve", "Bryan", "Tony", "Terry"};
  for (int i=0; i<user.size(); i++) cout << user[i] << '\t' ;
  
    cntAll=user.size(); 
    std::thread tt[cntAll];
     
    // очередь, желающих отдохнуть
    for (int i=0; i<user.size(); i++) {
         tt[i]=thread(setUser, ref(user[i]));
    }
    // потоки ресурсов (car & skis)
    std::thread t1(getSkis);
    std::thread t2(getCar);
    
    // присоединение потоков
    for (std::thread& ta:tt) {
        ta.join();
    }
    /*
    for (int i=0; i<user.size(); i++) {
         tt[i].join();
    }
    */
  t1.join();
  t2.join();
 
  return 0;
}
 
// иногда крашится при закрытии консольного окна (не по Enter) - после завершения работы main ??????????
// ??? потому что надо время для join???
1) есть ли какие замечания?
2) почему после окончания работы программы и при закрытии консольного окна не по Enter, а по крестику - иногда крашится?
3) нужно ли делать какое-либо ожидание для в потоке setUser, или глобального флага (value) на наличие 2х нужных элементов для отпуска- достаточно?
4) и можно ли как-то более красиво сделать?
(чувствую, что весь отпуск проходит на локе - что, вероятно не есть хорошо)
P.S.
5) можно ли это назвать кольцевым буфером (овбождение и запуск снова в работу машины и лыж)?.. может, лучше, действительно, буфер для накопления лыж и машины создать? - и из него вытягивать всё одновременно для user'a? (может они освобождаться не одновременно будут)... как?.. (типа такого или такого -- вероятно, действительно, и машину, и лыжи надо move'ить в поток потребителя ?? но не уверена)
P.P.S.
6) чувствую, что у меня ещё пока процедурная логика, но не правильная многопоточная... это если допустить, что лыжи и машину сначала делают на конвейере, потом пройдёт очередь, их использующая, потом в перспективе можно их сдать на склад (в общем, в потоках лыж и машины ещё всю их жизнь можно пропустить - не страшен ли такой длинный лок, который там висит ??)
если у кого появятся идеи...

Добавлено через 10 минут
честно говоря, по выводу выполняется, как будто, в очерёдности заданной главным потоком... хотя несколько выбросов было - нормально неупорядоченно по очереди user'ов (т.е. вероятно всё-таки асинхронно)
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
16.04.2021, 20:55
Ответы с готовыми решениями:

Как создать очередь потоков с несколькими workers?
-организовать threadpool с очередью и несколькими worker, которые будут выполнять функцию детекции...

Выполнение нескольких потоков. Каждый из них запускается с несколькими параметрами
Привет всем Помогите пожалуйста. Есть программа в ней поле Потоки. Ввожу допустим 100(значит...

Какие ресурсы использовать?

Как использовать ресурсы
Здравствуйте! Уже давно мучаюсь с ресурсами - не могу разобраться. раньше было так: ...

11
49 / 147 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
17.04.2021, 06:37  [ТС] 2
Цитата Сообщение от JeyCi Посмотреть сообщение
буфером (овбождение и запуск снова в работу машины и лыж)
в смысле не "буфер" в прямом C-смысле, а в плане - типа "буферная зона", где ожидается накопление ресурсов...
а то логика кажется немного прямолинейной, а не многопоточной... имхо... пока не много опыта в проектировании потоков
0
49 / 147 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
18.04.2021, 21:06  [ТС] 3
всё-таки в setUser cv_var1.notify_one(); - сообщаем ожидающему (в данном случае - 1)... в мыслях хотела сообщить и др. user'ам... - но их это не касается

Добавлено через 12 минут
P.S.
в любом случае ВАЖНО:
Long locks are implemented with mutexes and condition variables, so that a long lock can be held for a long time without affecting the performance of the program.
просто мьютексами можно обойтись лишь при блокировкие малого содержимого блокируемого участка... можно использовать и critical_section (c.s. objects can be shared only in a single process - в отличие от мьютекса, но в общем - их действие одинаково) - C.S. немного быстрее
0
49 / 147 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
19.04.2021, 13:01  [ТС] 4
1
Цитата Сообщение от JeyCi Посмотреть сообщение
вероятно, действительно, и машину, и лыжи надо move'ить в поток потребителя ?? но не уверена
не надо
ссылка
https://codereview.stackexchange.com/questions/128832/c11-blocking-queue-learning-exercise

Just Job take() should be fine, compiler will use NRVO optimization so that no copy will be done, copy constructor call would be elided. You could return by moving: return std::move(job);, but that can prevent elision.
Добавлено через 12 минут
2
Цитата Сообщение от JeyCi Посмотреть сообщение
(чувствую, что весь отпуск проходит на локе - что, вероятно не есть хорошо)
cppreference std::condition_variable
хотя в данном случае, как ему ещё проходить - если больше никто не может взять лыжи и машину (т.к. они в единичном экземпляре) - вот это добро и залочено до и после wait... только в wait lock снимается, чтобы shared_data могли быть modified для того, чтобы быть считанными для выхода из ожидания...

Добавлено через 10 минут
3
Цитата Сообщение от JeyCi Посмотреть сообщение
4) и можно ли как-то более красиво сделать?
т.к. cv использует только обычные мьютексы и обёрку unique_lock, то использовать какие-то др. примитивы синхронизации для реализации паттерна BlockingQueue (в рамках cv) не получится (только если что-то уж очень самодельное на low-level)... а т.к. использование просто мьютексов - загружает CPU, то в общем вариант cv+mutex+bPredicate и остаётся самым ресурсо-сберегающим... или проверка на empty() - если кого-то где-то не устраивает флаг - там где это уместно
4
при этом
Цитата Сообщение от DrOffset Посмотреть сообщение
Сообщение от zayats80888
Q Т.е. в интерфейсе не вседа можно предусмотреть поведение, требуемое бизнес-логикой?
A Можно предусмотреть, но это уже будет специализированное решение под заданные условия. Назвать такое решение "потокобезопасная очередь" уже не получится. Это будет какая-то сущность прибитая к логике задачи. И это прекрасно, потому что так будет возможность решить задачу максимально эффективно, хоть и не прибегая к красивой абстракции "потокобезопасной очереди".
не всё стоит абстрагировать...

Добавлено через 20 минут
5
и, действительно, по данной задаче - Condition Variables or Tasks
0
49 / 147 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
20.04.2021, 20:00  [ТС] 5
Цитата Сообщение от JeyCi Посмотреть сообщение
надо move'ить в поток потребителя
тут вообще др ситуация - банальная -
прередача в поток std::ref(obj) - можно использовать только если ref переживёт поток, которому принадлежит её объект... иначе dangling reference будет... - если есть такая вероятность - то надо std::move объект... всего-то
0
49 / 147 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
22.04.2021, 20:41  [ТС] 6
Цитата Сообщение от JeyCi Посмотреть сообщение
// очередь, желающих отдохнуть
поспешила - правильно так
C++
1
2
3
4
5
6
7
8
9
10
    // очередь, желающих отдохнуть
    for (int i=0; i<user.size(); i++) {
        //thread thr(setUser, ref(user[i]));
        //tt.emplace_back(std::move(thr));
        tt.emplace_back(thread(setUser, ref(user[i])));
    }
      // ...
     // присоединять можно так
    // call join() on each thread in turn
    for_each(threads.begin(), threads.end(), std::mem_fn(&thread::join));
над сутью MT - ещё подумаю - точнее над эффектом лотереи и корректном дизайне

Добавлено через 31 минуту
p.s.
... можно тред-пул задействовать
... конструкция учебного тред-пула

Добавлено через 2 часа 7 минут
однако
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
В пуле потоков лучше так не делать, он предназначен для коротких задач, ты не один им пользуешься. Создай std::thread
Добавлено через 3 часа 38 минут
p.p.s.
join
A thread is not joinable when it is default constructed or is moved/assigned to another thread or join() or detach() member function is called.
Not joinable thread can be destroyed safely.
0
49 / 147 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
23.04.2021, 23:14  [ТС] 7
если никто не хочет высказаться по многопоточной архитектуре... для инфо оставлю свой линк...
а стоит ли лыжи и машину разделять по потокам - пусть остаётся на суд разработчиков

Добавлено через 2 минуты
p.s.
1.1. Терминология параллельных вычислений
0
What a waste!
1607 / 1299 / 180
Регистрация: 21.04.2012
Сообщений: 2,726
24.04.2021, 00:18 8
JeyCi, весь тред и код не читал, но глядя на начало листинга сразу возник вопрос: при чём тут volatile?
0
49 / 147 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
24.04.2021, 08:33  [ТС] 9
Цитата Сообщение от gray_fox Посмотреть сообщение
весь тред и код не читал, но глядя на начало листинга сразу возник вопрос: при чём тут volatile?
поэтому и вопрос ... - прочиаете код - найдёте ответ или опровергните его целесообразность здесь, если она есть... зачем выдирать слово из контекста?.. - я на выдранные слова не отвечаю (уже отвечала) - интересуют целостные структуры логики и её применимости в той или иной реализации - Pro & Contra относительно выбранного инструментария...
гадать, чем она вам не нравится, - нет времени... (если у вас нет времени это объяснить)
0
49 / 147 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
27.04.2021, 19:21  [ТС] 10
забыла всё-таки отметить - задачу можно свести и переработать из задачи про Обедающих философов... и ещё проще не в std++17...
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
можно сделать при помощи виндовских мьютексов и WaitForMultipleObjects.
Добавлено через 3 часа 57 минут
p.s.
про move
_stackoverflow_com_questions/14856344/when-should-stdmove-be-used-on-a-function-return-value

So, to answer the question in the title, use std::move on a return value when you want it to be moved and it would not get moved anyway. That is:
• you want it to be moved, and
• it is an lvalue, and
• it is not eligible for copy elision, and
• it is not the name of a by-value function parameter.
Добавлено через 8 минут
+ Замечание по move semantics при операторе return в C++11
0
49 / 147 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
30.04.2021, 07:29  [ТС] 11
Цитата Сообщение от JeyCi Посмотреть сообщение
p.p.s. join
типичный пример
Кликните здесь для просмотра всего текста
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
// https://stackoverflow.com/questions/37891707/exception-when-joining-threads
 
// You shouldn't presize the vector with 5 default-constructed threads, then ignore those while push_back-ing additional ones. 
// Your join then tries to join the default-constructed threads and throws. 
// Get rid of the (5), or move it to a reserve call.
 
// That worked! I didn't think about the fact that making a with a size calls default constructors
 
/*
http://www.cplusplus.com/reference/thread/thread/join/ states that join will only throw invalid_argument if the thread is not joinable.
http://www.cplusplus.com/reference/thread/thread/joinable/ states that a thread is not joinable only if: 
•it was default-constructed. 
•it has been moved from (either constructing another thread object, or assigning to it). 
•either of its members join or detach has been called. 
*/
 
#include <iostream>
#include <vector>
#include <thread>
#include <chrono>
#include <mutex>
 
using namespace std;
 
mutex m;
 
void foo(int);
 
int main()
{
        vector<thread> threads;     //(5);  !!! default-constructed when (5)
        for (int i = 0; i < 5; i++)
        {
                threads.push_back(move(thread(foo, i)));
        }
 
        for (vector<thread>::iterator iter = threads.begin(); iter != threads.end(); iter++)
        {
                iter->join();
        }
}
 
void foo(int id)
{
    std::lock_guard<mutex> lock(m);       
        cout << "thread " << id << " started." << endl ;
        // Although it is interesting that the output is still ordered in the same way. Any ideas?
        // Sheduler switch context => ok randomness
        this_thread::sleep_for(chrono::seconds(1));
        cout << "thread " << id << " terminated."<< endl ;
}
0
49 / 147 / 33
Регистрация: 29.06.2019
Сообщений: 1,428
08.05.2021, 10:06  [ТС] 12
вот, кстати, класс, разбивающий функционал по функциям согласно Single_Responsibility - для IPC 3х потоков
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
// https://web-answers.ru/c/mnogopotochnost-sinhronizacija-treh-potokov-v.html
// многопоточность — синхронизация трех потоков в переполнении стека
 
/*
t1 а также t2 сделаем одну итерацию цикла, затем t3 сделаем одну итерацию, затем снова t1 а также t2 должен сделать один, то t3 должен сделать один.
Итак, вы видите, мне нужно t1 а также t2 делать вещи одновременно и после одной итерации, t3 должен сделать одну итерацию самостоятельно.
*/
 
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
 
using namespace std;
 
class SyncObj {
    mutex mux;
    condition_variable cv;
    bool completed[2]{ false,false };
    
    public:
    void signalCompetionT1T2(int id) {
        lock_guard<mutex> ul(mux);
        completed[id] = true;
        cv.notify_all();
    }
    void signalCompetionT3() {
        lock_guard<mutex> ul(mux);
        completed[0] = false;
        completed[1] = false;
        cv.notify_all();
    }
    void waitForCompetionT1T2() {
        unique_lock<mutex> ul(mux);
        cv.wait(ul, [&]() {return completed[0] && completed[1]; });
    }
    void waitForCompetionT3(int id) {
        unique_lock<mutex> ul(mux);
        cv.wait(ul, [&]() {return !completed[id]; });
    }
};
 
class MultiClass {
    public:
    void Run() {
        std::thread t1(&MultiClass::Calc1, this);
        std::thread t2(&MultiClass::Calc2, this);
        std::thread t3(&MultiClass::Calc3, this);
        t1.join();
        t2.join();
        t3.join();
    }
    private:
    SyncObj obj;
    void Calc1() {
        for (int i = 0; i < 10; ++i) {
        obj.waitForCompetionT3(0);
        std::cout << "T1:" << i << std::endl;
        obj.signalCompetionT1T2(0);
        }
    }
    void Calc2() {
        for (int i = 0; i < 10; ++i) {
        obj.waitForCompetionT3(1);
        std::cout << "T2:" << i << std::endl;
        obj.signalCompetionT1T2(1);
        }
    }
    void Calc3() {
        for (int i = 0; i < 10; ++i) {
        obj.waitForCompetionT1T2();
        std::cout << "T3:" << i << std::endl;
        obj.signalCompetionT3();
        }
    }
};
 
int main() {
    MultiClass m;
    m.Run();
    return 0;
}
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
08.05.2021, 10:06
Помогаю со студенческими работами здесь

Подскажите как использовать ресурсы
Подскажите как использовать ресурсы Вот допустим начал я писать в &quot;С&quot; и что бы мне все компоненты ...

Загрузить картинку в ресурсы и использовать ее
Извиняюсь за вопросы из разряда для начинающих в C++, но... Как загрузить картинку в файл...

Использовать ресурсы из отдельной сборки
Всем привет. Полазил по просторам тырнета и ничего не нашел. А может и искал не правильно. В общем...

Как использовать строковые ресурсы?
у меня есть таблица строк, в файле resource.h #define IDS_STRING101 101...

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

Как использовать ресурсы в консольном приложении?
Как использовать ресурсы в консольном C#? Задача такая, надо скомпиллировать проект, чтобы он в...


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

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

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru