Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.71/58: Рейтинг темы: голосов - 58, средняя оценка - 4.71
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524

Потоко-безопасная Очередь

05.04.2021, 20:16. Показов 12771. Ответов 106
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
... вырисовалась тема отсюда
Цитата Сообщение от zayats80888 Посмотреть сообщение
Хотя не сложно написать такую очередь(с раздельной блокировкой для "хвоста" и "головы") самостоятельно.
вот выполнила последнюю задачку отсюда - но не знаю, может лучше shared_mutex вместо просто mutex? правда на него unique_lock<shared_mutex> не ставится...
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
#include <iostream>
#include <thread>
#include <condition_variable>
#include  <mutex>
#include <chrono>
#include <queue>
 
using namespace std;
 
  bool bDone;
  mutex m;
  std::condition_variable cv;
 
int main() {
    int c = 0;
    bool done = false;
    queue<int> goods;
 
    thread producer([&]() {
        std::lock_guard<mutex> lock(m);
        for (int i = 0; i < 500; ++i) {            
            goods.push(i);
            c++;
            cv.notify_one();
        }
 
        done = true;
    });
 
    thread consumer([&]() {
        std::unique_lock<mutex> lock(m);
        while (!done) {
            while (!goods.empty()) {
                goods.pop();
                c--;
                while (!bDone) cv.wait(lock);
            }
        }
    });
 
    producer.join();
    consumer.join();
    cout << "Net: " << c << endl;
}
тут же не раздельная блокировка? - это FIFO, насколько понимаю...
а раздельная - вы, наверно, имеете ввиду для LIFO... наверно лучше на list выполнять?
sorry, что много вопросов - стою на распутье - даже в отдельную тему вынесла
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
05.04.2021, 20:16
Ответы с готовыми решениями:

Потоко-независимая очередь записывает 2е команды в одну ячейку. Почему ?
Доброго времени суток. Написал 2а приложения общяющихся между собой по TCP. Одно является сервером(планшет), второе клиент(windows). ...

Потоко-безопасный ObservableCollection с AddRange
Вообщем, я заполняю в нескольких потоках массив. на данный момент использую &quot;List&lt;ExpandoObject&gt; Вывод&quot; из-за...

Является ли boost::asio::tcp::acceptor потоко-безопасным ?
Мне сложно понять смысл того что написано в документации: ...

106
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
13.04.2021, 08:06  [ТС]
Студворк — интернет-сервис помощи студентам
oleg-m1973, ваши комменты (опускающие всех и вся) уже в который раз неадекватны в ситуациях, которые я в своём кодинге проходила уже не раз, - когда тебе пихают и 4 ядра и RAM за террабайт и всё в кармане - под гнилым девизом "у всех так" и "сейчас..." не зная "зачем"... так и идите со всеми... коль сапожники, делающие качественную обувь, вами презираемы больше, чем сапоги-схороходы, под которые собираете свой арсенал (вы что ещё мультиков не насмотрелись)... я всегда прежде всего за адекватность и соответствие!.. решений - задачам... от всего не застрахуешься ... а всё и не надо!!
Вы знаете как правильно тестируется многопоточность...
предлагаю, не опускать ветку в очередной раз до ваших предрассудков (заведите для этого, пожалуйста, отдельную ветку - для оскорблений смартфонов, ос, железа и чего хотите) -- напрягают ваши гуриные реплики (то же самое без вонючих аллегорий и метаморфозов и недосказанностей будет восприниматься более информативно)... я гурей, знающих только два слова и тележку высокомерия в адрес сапожников, без попыток снизойти до улаживания своих психо-эмоц-ых проблем вместо поливания грязью всех, - не-по-ни-маю... адекватных доводов у вас НЕТ, предмет вашей viewpoint не раскрыли... (просто начали вываливать свой негатив)
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
надо чётко контролировать, что именно ты делаешь, ну и худо-бедно представлять, как работает процессор, который ты программируешь.
- вот в том то и дело, что долгоиграющие задачи подгрузки из сети, отдаю на откуп потоку/ам, а остальная бизнес-логика необсуждаема здесь на ветке ! даже с гурями...
и не все задачи зависят от сети... (- я ведь тоже могу говорить недосказанности)... достал такой диалог с вам... достало ваше пренебрежение к теме и решениям с "предельной поезностью" под конкретные задачи...
хоть С/С++ и оперирует абстракциями, я задачи всегда ставлю конкретно, чего и вам желаю (а не надеяться на то, что все побегут обслуживать 4 ядра да ещё из ооооочень длинной очереди, потому что так сказал гуру-Олег, не снизойдя до логичных пояснений адекватности)... ради геморроя, где он не нужен... кому нужен - тот и платит невразумительными переключениями контекстов
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
13.04.2021, 09:23
Цитата Сообщение от JeyCi Посмотреть сообщение
достал такой диалог с вам... достало ваше пренебрежение к теме и решениям с "предельной поезностью" под конкретные задачи...
Ок, не буду мешать
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
13.04.2021, 10:36  [ТС]
я же не космодром проектирую и не прыгну выше чужого сервера и скорости провайдера, а свой VPN мне дома уж точно не нужен... всё для Home-PK и автоматизации каждодневной рутины... а реальность практически для каждого бытового user'a такова:
A major weakness of the Win32 API is that the queuing model is undefined (i.e. neither Priority nor FIFO). According to Microsoft this is done to improve performance.
-- и не полезут они на многопоточный Linux... глупо пытаться серверные технологии внедрять в каждую затычку каждой квартиры (ради приватизации доходов и коллективизации расходов) - мир чище не станет... (и морально-психологически в том числе) - не умея отделять шелуху от зёрен, и не отделять логику от целесообразности и культуры речи...
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
14.04.2021, 19:40  [ТС]
да, и кстати... видимо, на этом форуме не всегда делятся информацией, а периодически - дезинформацией...
Цитата Сообщение от JeyCi Посмотреть сообщение
поэтому и разделяем бизнес-логику по потокам...
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Нет.
... в прошлой ветке пришлось линками доказывать этому форумчанину ошибочность его навязываемой "убеждённости" в том, что бинарный семафор и мьютекс - - одно и то же (по его мнению), а по сути - далеко НЕ всегда взаимозаменяемы... перекос его взглядов встречается уже не первый раз... категоричность его возможного бреда (не подтверждаемого им логически, настолько обтекаемого в его абстрактности, что даже логика вынуждена теряться в догадках) - оставляет неприятный осадок и кучу мусора в теме... часто выводы гнилыыыыые!.. а разгребать мусор в своём восприятии придётся его оппонентам... не пытайтесь познакомиться с темой через таких гурей! - часто лочить/усыплять поток не надо!.. проектируйте тщательно, внимательно и осторожно!.. чтобы потом такие страховые агенты не выносили вам мозг... - своим враньём пытаясь формировать ваше представление о предмете... умышленно сливая на вас свой негатив и свою дезинформацию... не позволяйте замусоривать вам мозг ... грустно делать такие выводы о человеке, который что-то знает, но почему-то несёт и то, чего не знает, - с тем же видом... а ещё грустнее будет кодить с его пустым PR'ом...
проектирование потоков вообще целесообразно при удалённой локализации клиента и сервера - для доступа к серверу нескольких клиентов и взаимодействию по сети...
===
"качество инфо - это качество итогового продукта!"
===
но не всё так страшно:
Howard Hinnant also pointed out an interesting fact about performance, you can check this link if you are interested. He addresses performance concerns and shows that std::lock can be implemented efficiently, I can also recommend to read all the comments in that post
===
и лочить 2 мьютекса без deadlock'a можно было ещё до появления scoped_lock
ссылка
https_stackoverflow_com/questions/17113619/whats-the-best-way-to-lock-multiple-stdmutexes/17113678

===
многопоточность бывает полезна, но, используя, сигнальные механизмы IPC- не стоит забывать, что это всего лишь сигнал - важно, чтобы его не перехватил НЕ-target, а target, чтобы принял при любом развитии событий...
===
при этом здесь
Для передачи исключений между потоками, необходимо ловить их в функции потока и хранить их где-то, чтобы, в дальнейшем, получить к ним доступ.
так же и здесь, как в мозге, - не храните мусор...
===
и всё же бывает и lock-free приносит пользу
PPS. В Boost-е обнаружились такие нужные мне сейчас вещи, как spin-lock-и и lock-free очереди.
===
адекватный тест разницы между одним и двумя потоками

===
4 совета банально просты:
1. Потоки не одноразовые  —  их нужно присоединять повторно!
2. Используйте флаг для обозначения момента прекращения потока
3. Используйте мьютексы при обращении потоков к общим ресурсам и данным
4. Учитывайте аппаратное ограничение многопоточности
===
ИТОГО:
обмен мнениями хорошо - уход от реальности плохо...
остановлюсь на примере ThreadSafeQueue из линка, уже бывшего
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
15.04.2021, 08:07  [ТС]
может и не простое чтиво , но очень раскладывающее всё по полочкам:
UNIX взаимодействие процессов
Главная проблема — что делать, если нужный элемент еще не готов. Все, что нам остается и что мы делаем в листинге 7.4, — это повторять операции в цикле, устанавливая и снимая блокировку и проверяя значение индекса. Это называется опросом (spinning или polling) и является лишней тратой времени процессора.

Мы могли бы приостановить выполнение процесса на некоторое время, но мы не знаем, на какое. Что нам действительно нужно — это использовать какое-то другое средство синхронизации, позволяющее потоку или процессу приостанавливать работу, пока не произойдет какое-либо событие.
- в принципе эту главную проблему и решили на ветке использованием cv+mutex+flag... оптимизация допустима
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
15.04.2021, 21:56  [ТС]
Цитата Сообщение от JeyCi Посмотреть сообщение
а то я что-то с Actor'ами задумалась
Модель Акторов и C++: что, зачем и как?
Так же я подозреваю, что пришествие настоящих многоядерных CPU сделает программирование параллельных систем с использованием традиционных мьютексов и разделяемых структур данных сложным до невозможности, и что именно обмен сообщениями станет доминирующим способом разработки параллельных систем.
как вариант - boost не нужен
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
// https://stackoverflow.com/questions/19593427/actor-calculation-model-using-boostthread
/*
I have added event queue code into your example.
You have concurrent queue in both EventQueueImpl and Actor, but for different types. 
It is possible to extract common part into separate entity concurrent_queue<T> which works for any type. 
It would be much easier to debug and test queue in one place 
than catching bugs scattered over different classes
*/
// http://stackoverflow.com/questions/19593427/actor-calculation-model-using-boostthread
// g++ -std=c++11 -O3 -mtune=native -Wall -pedantic -pthread main.cpp -lboost_system -lboost_thread -lboost_chrono && ./a.out
 
#include <thread>
#include <future>
#include <functional>
#include <atomic>
#include <iostream>
#include <cassert>
#include <string>
#include <queue>
 
using namespace std;
 
template<typename T>
class concurrent_queue
{
    std::queue<T> q;
    mutable mutex m;
    mutable condition_variable c;
 
    void dequeue(T &result)
    {
        result = q.front();
        q.pop();
    }
public:
    void push(const T &t)
    {
        lock_guard<mutex> l(m);
        q.push(t);
        c.notify_one();
    }
    void pop(T &result)
    {
        unique_lock<mutex> u(m);
        c.wait(u, [this]{ return !q.empty(); } );
        dequeue(result);
    }
    bool try_pop(T &result)
    {
        scoped_lock u(m);
        if(q.empty()) return false;
 
        dequeue(result);
        return true;
    }
    template<typename Duration>
    bool try_pop(T &result, Duration timeout)
    {
        unique_lock<mutex> u(m);
        c.wait_for(u, timeout, [this]{ return !q.empty(); } );
 
        if(q.empty()) return false;
 
        dequeue(result);
        return true;
    }
};
 
struct Actor
{
    typedef function<int()> Job;
 
    concurrent_queue<Job> d_jobQueue;
 
    bool d_keepWorkerRunning;
    thread d_worker;
 
    Actor()
        : d_keepWorkerRunning(true), d_worker(&Actor::workerThread, this)
    {}
 
    ~Actor()
    {
        execJobSync([this]()->int
        {
            d_keepWorkerRunning = false;
            return 0;
        });
        d_worker.join();
    }
 
    void execJobAsync(const Job& job)
    {
        d_jobQueue.push(job);
    }
 
    int execJobSync(const Job& job)
    {
        std::string error;
        promise<int> promise;
        future<int> fut= promise.get_future();  // unique_
 
        d_jobQueue.push([&]() -> int
        {
                int rc = job();    
                promise.set_value(rc);
                return 0;
        });
 
        int rc = fut.get();
        return rc;
    }
 
    void workerThread()
    {
        Job job;
        while (d_keepWorkerRunning)
        {
            d_jobQueue.pop(job);
            job();
        }
    }
};
 
typedef int EventImpl;
typedef concurrent_queue<EventImpl *> EventQueueImpl;
 
int main()
{
    using namespace std;
    cout << "start" << endl;
    {
        Actor x;
        EventQueueImpl eventQueue;
        for(int i=0; i!=16; ++i)
        {
            cout << i << endl;
            x.execJobSync([=]() -> int { 
                        cout << "Sync " << i << endl; 
                        return 0; });
            x.execJobAsync([=]() -> int { 
                        cout << "Async " << i << endl; 
                        return 0; });
        }
        EventImpl e;
        x.execJobSync([&]() -> int {
            cout << "gen event" << endl; 
            eventQueue.push(&e); 
            return 0;
        });
 
        EventImpl *result = nullptr;
        eventQueue.try_pop(result, chrono::milliseconds(10*1000));
        cout << (&e == result) << endl;
    }
    cout << "end" << endl;
}

но в U++ есть класс Event

Добавлено через 1 час 27 минут
Почему цикл ест так много CPU?

Добавлено через 17 минут
бывает и такое в STD:
Цитата Сообщение от Юрий Мамонтов Посмотреть сообщение
Переделал только ОДНУ переменную в атомик - скорость упала раз в 10...
0
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
15.04.2021, 22:07
Цитата Сообщение от JeyCi Посмотреть сообщение
бывает и такое в STD
atomic в std:: напрямую использует возможности текущей архитектуры. Так что говорить, мол, "это в STD" как минимум спорно.

Кроме того:
Цитата Сообщение от DrOffset Посмотреть сообщение
обычно решения, хорошо работающие с блокировками плохо работают с lock-free и наоборот.
Т.е. если у человека из цитаты была именно такая ситуация, то нет ничего удивительного, что так получилось. Ни у кого еще в этой жизни не получилось нормально забить гвоздь микроскопом.
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
16.04.2021, 06:34  [ТС]
спасибо за view... и дополню цитатой вашей же
Цитата Сообщение от DrOffset Посмотреть сообщение
вообще думать о lock-free, нужно сначала аргументированно доказать, что производительность страдает именно из-за блокировки.
Недостаток экспертизы может привести к колоссальным потерям времени (в любом смысле, но в первую очередь времени программистов) в будущем.
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
16.04.2021, 20:46  [ТС]
Цитата Сообщение от DrOffset Посмотреть сообщение
Потому что нельзя освобождать блокировку перед уведомлением.
а если её нет, то и не думать о ней... имхо... если notify идёт из main-thread, то не обязательно сигналить под локом
Цитата Сообщение от zayats80888 Посмотреть сообщение
Настоятельно рекомендую вдумчиво прочитать книгу Уильямса("Параллельное программирование на С++ в действии").
да, есть нюансы
Э.Вильямс - Thread-safe Queue
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
// Листинг 4.5. Полное определение класса потокобезопасной очереди на базе условных переменных
// Энтони Уильямс Параллельное программирование на С++ в действии - c.119
 
#include <iostream>
#include <queue>
#include <memory>
#include <mutex>
#include <condition_variable>
 
using namespace std;
 
template<typename T>
class threadsafe_queue
{
private:
    mutable std::mutex mut; // Мьютекс должен быть изменяемым
    std::queue<T> data_queue;
    std::condition_variable data_cond;
    
public:     
    threadsafe_queue(){}
    
    threadsafe_queue(threadsafe_queue const& other) {
        std::lock_guard<std::mutex> lk(other.mut);
        data_queue=other.data_queue;
    }
    void push(T new_value) {
        std::lock_guard<std::mutex> lk(mut);
        data_queue.push(new_value);
        data_cond.notify_one();
    }
    void wait_and_pop(T& value) {
        std::unique_lock<std::mutex> lk(mut);   // другие потоки могут хранить неконстантные ссылки на объект и вызывать изменяющие функции-члены, которые захватывают мьютекс.Поэтому захват мьютекса –это изменяющая операция, следовательно, член mut необходимо пометить как mutable ������, чтобы его можно было захватить в функции empty() и в копирующем конструкторе.
        data_cond.wait(lk,[this]{return !data_queue.empty();});
        value=data_queue.front();
        data_queue.pop();
    }
    std::shared_ptr<T> wait_and_pop() {
        std::unique_lock<std::mutex> lk(mut);
        data_cond.wait(lk,[this]{return !data_queue.empty();});
        std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
        data_queue.pop();
        return res;
    }
    bool try_pop(T& value) {
        std::lock_guard<std::mutex> lk(mut);
        if(data_queue.empty())
        return false;
        value=data_queue.front();
        data_queue.pop();       
        return true;
    }
    std::shared_ptr<T> try_pop() {
        std::lock_guard<std::mutex> lk(mut);
        if(data_queue.empty())
        return std::shared_ptr<T>();
        std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
        data_queue.pop();
        return res;
    }
    bool empty() const
    {
        std::lock_guard<std::mutex> lk(mut);
        return data_queue.empty();
    }
};
 
int main()  
{
    threadsafe_queue<int> tsq;
    for (int i=0; i<5;i++) tsq.push(i);
    for (int i=0; i<5;i++) cout << *tsq.wait_and_pop() << endl;
    
  return 0;
}

===
просто есть в ref. какой-то try_lock
Attempts to lock the mutex, without blocking
C++
1
int try_lock( Lockable1& lock1, Lockable2& lock2, LockableN&... lockn);
If a call to try_lock results in an exception, unlock is called for any locked objects before rethrowing.
- пример здесь
использовать по сути предлагают так
C++
1
2
3
4
5
6
7
8
if (v_guard.try_lock() == true)
{
  // locked
}
else
{
  // not locked
}
-- но уж это дело, наверно, совсем бесполезное (только если в cout успеть пропихнуть shared_data или не успеть), а ловить исключение вместо блокировки не понятно зачем??... если, например, data_processing ещё не дошло до мьютекса (одного из мьютексов), то мне ведь нужен не exception, а подождать... имхо

Добавлено через 18 минут
или
if you specify a second parameter like this std::lock_guard<std::mutex> guard(_mutex, std::try_to_lock) the behaviour is changed to act like std::try_lock rather than std::lock


Добавлено через 9 минут
ок, он мне пока не нужен, т.к. используется для создания timed_lock() - тогда понятно...
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
18.04.2021, 21:38  [ТС]
Producer/Consumer На атомарных типах еще так

Добавлено через 33 минуты
Цитата Сообщение от JeyCi Посмотреть сообщение
проектирование потоков вообще целесообразно при удалённой локализации клиента и сервера - для доступа к серверу нескольких клиентов и взаимодействию по сети...
тут, вероятно, ещё моё неполное знакомство с темой - Benefits of threads - тут же и Limitations
Multithreaded programming is useful for implementing parallelized algorithms using several independent entities. However, there are some cases where multiple processes should be used instead of multiple threads.
Many operating system identifiers, resources, states, or limitations are defined at the process level and, thus, are shared by all threads in a process. For example, user and group IDs and their associated permissions are handled at process level. Programs that need to assign different user IDs to their programming entities need to use multiple processes, instead of a single multithreaded process. Other examples include file-system attributes, such as the current working directory, and the state and maximum number of open files. Multithreaded programs may not be appropriate if these attributes are better handled independently. For example, a multi-processed program can let each process open a large number of files without interference from other processes.
... в общем, остаётся только heavy-maths для CPU-bound operations... даже video-audio-stream - всё-таки IO-bound (часты отзывы, что MT - не ускоряет даже Drawing)
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
19.04.2021, 09:59  [ТС]
ИТОГО:
1) BlockingQueue - просто потому что надо блокировать (синхронизировать) при помещении/изъятии элемента из очереди
2) Кольцевой буфер - на опр. количество мест - чтобы не было переполнения буфера - что в принципе и реализовал TRam_ в посте, отмеченном ответом к теме (на 10 мест)
3) MessageQueue делается так же, как просто thread-safe Queue - бывает синхронная и асинхронная
(т.е. shared-memory становится просто элементом для асинхронной реализации message-passing через обычную queue)...
Java-презентация - главная идея, как всегда, sender-receiver, а что с каких портов и на какие слоты - это уже нюансы имеющегося железа, языка и предпочтений разработчика (по п.4 кстати не уверена, что для смартфонов пишут на C++ в современном мире, раз уж гур выше ничего кроме 4х-ядерных смартфонов не знает)...
4) человек, завёрнутый на смартфонах, вносил путаницу в тему, умышленно пытаясь перекосить ход темы, -- отрываясь от логики в сторону железа... таким ходом и до осциллографов и различных медицинских приборов не далеко добраться - где вообще отсутствует RAM, и надо умудряться весь DataFlow просчитать и вывести на экран через к.-л. CPU... (а с качеством инфо от гура [oleg-m1973, ] - он даже побъёт рекорды covid'а...)
(я тему открывала не под названием "как увеличить тормозной пусть, чтобы пройти его быстро", а именно по названию в заголовке темы; все умышленные извращения темы и развешивание на неё лапши - предлагаю читающим отправлять не на мусороперерабатывающий завод, а человеку, несущему мусор, лично в его мозг)
здоровый диалог и ключевые моменты - всегда приветствуются в теме
5) MSVC Parallel Programming in Visual C++... (поскольку не люблю пока Qt с такой её support)
6) отдельно стоит проработать Cancellation всех потоков на всякий случай (в примерах TS-Queue обычно не указаны, как и exception_catching)... но в норм. Фреймворке это обычно заложено в родную реализацию потоков/многопоточности (как, например, в U++, с которым знакома)
0
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
19.04.2021, 14:41
Цитата Сообщение от JeyCi Посмотреть сообщение
Кольцевой буфер - на опр. количество мест - чтобы не было переполнения буфера
Это вообще не кольцевой буфер (кольцевой - это если бы первый поток продолжал непрерывно поглощать элементы даже при полностью остановленном втором потоке, перезаписывая самые старые). Это просто "буфер на 10 мест", не более.
1
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
19.04.2021, 22:08  [ТС]
Цитата Сообщение от zayats80888 Посмотреть сообщение
И об этом я тоже написал, while (!done ) вклинится, например, после первых двух элементов
перенесла его модификацию в consumer и, конечно, же ничего не notify - atomic лишний раз не лочила (как были советы) -- не тот флаг, чтобы его лочить здесь, атомарность норм... имхо...
Главное! вспомнила своЙ ход логической мысли -- всегда сначала проверяю на выход из while (просто всегда return'ила, а тут break нужен) -- и потом всё по коду потока... но можно и в конце проверять... вобщем, чтобы не оборачивать в if-else и не вспоминать потом, что else...
и отпринтовала для наглядности - очередь без ограничений на количество - зато стройная логика - своя родная - для завершения темы :
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
#include <iostream>
#include <thread>
#include <condition_variable>
#include  <mutex>
#include <queue>
 #include <atomic> 
 
using namespace std; 
 
int main() {
    volatile int c = 0;
    volatile std::atomic_bool  done = false;
    queue<int> goods;
    mutex m, mp;
    std::condition_variable cv;    
    
    thread producer([&]() {               
        for (int i = 0; i < 500; ++i) { 
            std::unique_lock<mutex> lock(m);                   
            goods.push(i);
            c++;
            cv.notify_one();
            // to show
            {
                std::lock_guard<mutex> lock(mp);
                cout << c << " p " << endl;
            }
            
        }
        done = true;
 
    });
 
     thread consumer([&]() {
        int i=0;
        std::unique_lock<mutex> lock(m);
            while (true) {
                if( done && goods.empty()) { break; }   // OK
                
                cv.wait(lock, [&]{return !goods.empty();}); 
                goods.pop();
                c--;
                // to show
                i+=1;               
                {
                    std::lock_guard<mutex> lock(mp);
                    cout << c << " c " << i << endl;
                }
            }
    });    
 
    producer.join();
    consumer.join();
    cout << "Net: " << c << endl;
}
суть понятна - всем спасибо - когда был конструктив...
P.S.
Need of Event Handling - КАРТИНКА красивая... нарисовать всегда полезнее, чем сразу кодить ... а Signal for Condition или Event - это уже, действительно, нюансы (хоть 1-е и может быть быстрее, чем 2-е)
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
19.04.2021, 22:32
Цитата Сообщение от JeyCi Посмотреть сообщение
атомарность норм... имхо...
Нет, и я и и oleg-m1973 об этом говорили.
oleg-m1973 показал вам правильный пример в данном контексте.

Не норм, т.к. возможен следующий сценарий:
producer пушит последний элемент и перед строкой 30 система его переключает(усыпляет) ->
consumer забирает последний элемент и переходит на следующей итерации в цикл ожидания(done всё еще false) ->
producer просыпается, выставляет флаг done и завершает работу.
Только consumer теперь разбудить некому, а даже если будет ложное пробуждение, то снова уснет, т.к. done не проверяется.

Не по теме:

P.S. зачем volatile???

1
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
19.04.2021, 22:45  [ТС]
Цитата Сообщение от zayats80888 Посмотреть сообщение
зачем volatile???
чтобы RVO не оптимизировало его... т.к. переходит в main...
а по ТЗ - вариант с 2мя cv мне тоже кажется ещё более логичным (что и отметила ответом к теме)
p.s.
кто чего говорил - искать не буду --- наверно, плохо говорил, раз 5 страниц наговорили
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
20.04.2021, 08:01  [ТС]
Цитата Сообщение от zayats80888 Посмотреть сообщение
даже если будет ложное пробуждение, то снова уснет, т.к. done не проверяется.
значит строка 40
C++
1
while (!done) {cv.wait(lock, [&]{return !goods.empty();});  break;}
значит while (!done) уже не помешает!? - значит моя провекрка была не лишняя!
-- что ж такие нападки были от моей формулировки "от spurious wakeups" ? - риторический вопрос - если нет ответа...
я просто по незнанию - не то проверяла ... и никто не знал (не думал) тоже пока отмазывали друг друга...
ну и ладно... оставайтесь друг с другом, если в кодинг не хотите идти с красотой решения и стройной логикой...
p.s.
но эту статью я всё-таки как-нибудь ещё перечитаю не по диагонали - Don’t wait without a condition
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
20.04.2021, 08:49
Цитата Сообщение от JeyCi Посмотреть сообщение
значит строка 40
Нет... Вы так и не поняли, как работает ожидание с предикатом.
cv.wait(lock, [&]{return !goods.empty();}) - это цикл, из которого можно выйти только, если предикат вернёт true.
Проверяется он при каждом пробуждении и не важно, ложное оно или нет. В той ситуации, которую я описал в предыдущем посте из этого ожидания не выйти(я упомянул про ложное пробуждение только потому, что в вашем последнем коде это единственное, что может пробудить поток в этом случае и оно не является проблемой).
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
20.04.2021, 09:54  [ТС]
Цитата Сообщение от zayats80888 Посмотреть сообщение
cv.wait(lock, [&]{return !goods.empty();}) - это цикл, из которого можно выйти только, если предикат вернёт true.
- вот и начали с того, на чём закончили! - так нужна ли вам лишняя провекрка по выходу из ожидания?.. хотите делайте, не хотите не делайте, - и это никак не зависит от того, что вы пишете про меня... не имея доказательст! вы сами уже запутались...
p.s.
zayats80888, done может стать только после того, как producer поместил очередной элемент (даже если последний) - не раньше... а consumer пока !done - в любом случае дождётся нужного true в предикате, если done ещё не выставлено... !! даже если планировщик переключит контекст до выставления done - consumer обработает свой последний элемент, когда истинность пробуждения будет подтверждена предикатом, выдавшем наконец true...
планировщик, насколько помню, не такой дурной, как вам кажется
... и переключает контекст в строго логичных местах, а не вдруг, - в частности на каждом цикле итерации... можно, вероятно, и yield попробовать сделать, но это излишне здесь... и когда цикл consumer'a пойдёт на следующий свой loop - он уже подхватит то, что в параллели сделал producer (модифицировал done в true) - а именно своей проверкой if...
p.p.s.
вобщем, zayats80888, спорьте, пожалуйста, дальше с планировщиком, ему объясняя, что он ничего не понимает, а не мне... а проверять лишний раз атомарный флаг (который в принципе к вашему предикату не имеет отношения) или не проверять - решайте сами... или делайте на 2х cv... или всё-таки библиотеку пролистайте - чтобы определиться: так выйдет он у вас без лишней проверки или не выйдет... и чего вы так боитесь этих spurious, если wait закончится по true...
у вас явное недопонимание Планировщика...
(и вы, как и предыдущий гуру вносите бред в тему) -- приложите результаты тщательных испытаний описываемой вами ситуации, чтобы хотя бы отразить её возможность практическую, а не то что вам кажется... а покА вы спорите сам с собой... обвиняя меня в своих непонятках... - очередной троль? -- вы выражения подбирайте, отражающие суть, а не ваши иллюзии, которые ещё и не по теме... культура речи - и в смыслах передаваемых! (если вы не в курсе)... - ложь - есть ложь!
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
22.04.2021, 14:04  [ТС]
TheCalligrapher в теме Многопоточность. Не работает condition_variable очень тонко подметил...
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Однако никто вам не гарантировал, что когда вы сделаете condition.notify_one() в первом потоке, то второй поток резко проснется и двинется вперед и успеет захватить simple_mutex до того, как первый поток сам успеет перезахватить simple_mutex.
Именно это и просиходит в вашем случае. Первый поток освобождает simple_mutex и тут же захватывает его снова. Второй поток успевает пройти ожидание в condition, но не успевает захватить simple_mutex. Потому он и вечно стоит на месте.
Примитивным способом решения проблемы будет sleep в первом потоке (таким образом вы фактически говорите планировщику "дай другим потокам поработать"). Более грамотным - заставить первый поток ждать, пока второй не сделает свою работу. Например, на еще одном condition variable.
и по дизайну истинной многопоточности
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
В параллельном программировании очередность прихода конкурирующих потоков на mutex - это типичный race condition. Правильно реализованный код не может полагаться на специфические проявления race conditions, т.е. на очередность прихода потоков на mutex. Другими словами - это не нужно даже пытаться реализовывать, ибо завязанность на такую очередность является грубой ошибкой дизайна, которая приведет только к проблемам в будущем.
... хотя, конечно, "синхронизация - враг многопоточности"...
(и проблема выше всё-таки проблема асинхронного исполнения на ограниченном количестве ядер... или на Linux, который в принципе, сколько угодно потоков может открыть, и иногда даже сам обеспечит атомарность счётчиков (например, семафоров), win-софт в большинстве своём однопоточен - особенности ОС могут быть критичны при выборе подхода и библиотеки для реализации MT, о чём имеются скупые ремарки в сети)

Добавлено через 5 минут
U++ CV - на заре молодости был таким
ConditionVariable (posix semantics, implemented for Win32 using per-thread semaphores)
- cv на семафорах - немного не тот cv для real performance (уже обсуждалось)

Добавлено через 10 минут
а вообще, в непростом чтиве (уже обсуждалось) - в главе 8.5 расписан пример использования sleep чтобы дать время др. потоку для перезахвата мьютекса...
(если бы пустословы и приверженцы буллинга, защитники первых, не перекашивали ветку - легче было бы проследить ключевые моменты и линки)

Добавлено через 2 минуты
TheCalligrapher'у отдельный respect за мат.часть
0
263 / 152 / 33
Регистрация: 29.06.2019
Сообщений: 1,524
22.04.2021, 20:34  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
То ли дедлок....
Deadlock
The guidelines for avoiding deadlock all boil down to one idea: don't wait for another thread if there's a chance it's waiting for you
1) Avoid nested lock
2) Acquire locks in a fixed order
3) Avoid a call to user-supplied code while holding a lock
4) Use a lock hierarchy
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
22.04.2021, 20:34
Помогаю со студенческими работами здесь

Сформировать односвязную очередь из элементов, которые входят в очередь Q1, но не входят в очередь Q2
Составить программу обработки динамической структуры данных: сформировать односвязную очередь Q из элементов, которые входят в очередь Q1,...

Не безопасная форма
Привет, пользуюсь менеджером паролей Lastpass, и мне интересно, что такое не безопасная форма? У разработчика спрашивал, но он объяснил что...

Безопасная работа
_http://www.webpark . ru/comments.php?id=45545

Безопасная регистрация
Всем привет. Вот я собственно попытался сделать систему регистрации, в PHP пока еще не особо силен, только понемногу начинаю учится. ...

Безопасная авторизация
Здравствуйте! Есть приложение клиент, которое обращается к wcf сервису, чтобы то произвело какие то действия. Клиент написан под...


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

Или воспользуйтесь поиском по форуму:
100
Ответ Создать тему
Новые блоги и статьи
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