1681
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записl
|
: Апрель 01, 2011, 12:03
|
Мне кажется тема зашла в тупик, на мой взгляд гораздо лучше/полезнее было бы обсудить вот это В смысле, можно создать внутренний буфер (массив тобиш). Пока читатели читают из одной ячейки массива, писатель, чтоб не простаивать пишет в следующую, и после того как запишет устанавливает индекс для чтения с актуальными данными. При этом в следующий раз писатель будет выбирать новый индекс для записи с учётом того, откуда читают данные читатели. Короче, читатели всегда будут знать где актуальные даные, а писатели будут знать куда писать, чтобы не было простоев. Конечно в новой теме Давайте создадим, как предлагаете назвать? И ещё для этого, если использовать только абстрактные rwlocker нужно мне бы хотелось знать логику их работы.
|
|
|
1682
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Апрель 01, 2011, 12:00
|
Про Ваш мьютекс. Там опять присвоение, и оно опять компрометирует атомарность:
T1, T2 пришли, захватили ключи 0 и 1, key = 1 T1 отработал и ушёл, key = -1 T2 решил сделать --key, и завис навсегда (между key = -1 и -2)
Нет, не зависнет он навсегда, поскольку key-- будет только тогда, когда tmp > 0 lock_read () { while (true) { scoped_lock sl (m_mutex); if (!m_writer) { ++m_readers; return; } } }
А что такое scoped_lock ? И как он устроен?
|
|
|
1683
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 31, 2011, 14:17
|
понятно, что имея настоящий мьютекс можно реализовать всё, что угодно. На этом этапе математики зачехляют линейки, а программисты идут писать тесты. Правда код я не проверял, сегодня только придумал вечером, так что критику интересно бы услышать Крутой
Хорошо, предположим, что у нас есть такой мьютекс (реализованный без CAS) И как с помощью него сделать lock-free? Вот пример самого мьютекса: C++ (Qt) class mutex { atomic_int key = -1; public: lock () { int tmp = 0; do { tmp = ++key; if (tmp) key--; } while (tmp != 0); } unlock () { key = -1; } };
|
|
|
1684
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 31, 2011, 11:03
|
Нужно увеличивать скважность импульса, например, отказаться от остатка кванта
кажется я понимаю! ..да нет, кого я обманываю.. Но интересный вариант Вы описали, правда смущает ограничение на одного пишущего и завис писателя, если поток читателей постоянный. Но в реализации подкопаться не к чему (кроме опечатки . А работает это потому, что каждая атомарная переменная используется только в атомарных операциях, а m_ax всё время компрометировал атомарность присвоением. Если отказаться от присвоений, то задача становится проще, потому что огород городить сложнее. 1) Ну "только один писатель" вполне usable, а вот без pending (запрос на запись) прожить трудно.
Кстати, зачем write_pending, почему нельзя просто ставить write (как у Akon, только CAS вместо fetchAndXor)? Ну то есть чтобы читатели не подвесили писателя, как я понял понял, но вроде бы одного write достаточно и короче получится, нет? Собственно с CAS это другой класс задач уже, тут можно уже делать wait-free, не то что спинлоки (кстати, CAS сам по себе такое же wait-free, как и атомарный ++). Теперь по сути задачи, которую решал m_ax: как сделать рв-спинлок без CAS? Как известно в математике и некоторых дугих религиях, задача станет ещё проще, если отказаться от задачи. А именно, реализовать обычный, а не рв-лок, например так: C++ (Qt) struct mutex { atomic_int n = 0; int level = 1; lock () { int nn = ++n; //atomic pre increment while (nn != level); } unlock () { level = nn + 1; //non-atomic, but protected by our newborn mutex! } };
понятно, что имея настоящий мьютекс можно реализовать всё, что угодно. На этом этапе математики зачехляют линейки, а программисты идут писать тесты. Правда код я не проверял, сегодня только придумал вечером, так что критику интересно бы услышать А в функции unlock, nn - это что? Если это тот atomic_int n, то тогда такая ситуация: Первый поток вызывает lock (n = 1, level = 1), но до unlock ещё не успевает дойти. В этот момент другой поток вызывает lock (n = 2, level = 1). Соответственно он не проходит и крутится в цикле. Затем, первый поток вызывает unlock (level = 2+1 = 3), что не освобождает второй поток, который продолжает крутится в цикле. Лучше изменить unlock так: C++ (Qt) void unlock () { level++; }
|
|
|
1685
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 30, 2011, 13:11
|
m_ax, просьба: давайте по пустому не месить и форум понапрасну не утомлять. Догнать а что же такое STATE_READ_AND_WRITE - мудрено. Что-то типа "Rat but blue"? C++ (Qt) static bool acquireRead(AtomicInt &counter, AtomicInt &state) { int tmp = (state |= STATE_READ); if (tmp == STATE_READ) {
Как уже не раз говорилось, между этими 2-мя строчками может произойти многое, "просто if" здесь не катит (поезд уже ушел) Я уважаю желание "докопаться самому" и с подозрением отношусь к "начитавшимся классов", но чего ж об один камень 10 раз спотыкаться? Зачем упорно избегать CAS который предназначен для решения таких коллизий? Зачем рыть себе яму привлекая вторую переменную за которой надо (мучительно) следить? Да, Вы правы. Пора завязывать с этой темой) Всем спасибо за обсуждение)
|
|
|
1686
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 29, 2011, 19:12
|
R0 читает; state == STATE_READ R1 читает; state == STATE_READ и дочитывает; state == STATE_FREE W0 начинает писать (в то время как R0 все еще читает)
Согласен Исправил этот момент, введя счётчик читающих потоков: m_counter. Теперь state = STATE_FREE только когда все читатели перестанут читать: C++ (Qt) #ifndef SPINLOCK_H #define SPINLOCK_H #include <assert.h> #include <cstdatomic> template <class T> class SpinLock { public: typedef atomic<int> AtomicInt; SpinLock() : m_key(-1), m_state(STATE_FREE), m_counter(0) {} void setData(const T &data) { while (!acquireWrite(m_key, m_state)); m_data = data; releaseWrite(m_key, m_state); } T getData() const { while (!acquireRead(m_counter, m_state)); T data = m_data; releaseRead(m_counter, m_state); return data; } private: enum { STATE_FREE = 0, STATE_READ = 1, STATE_WRITE = 2, STATE_READ_AND_WRITE = 3 }; AtomicInt m_key; AtomicInt m_state; AtomicInt m_counter; T m_data; //------------------------------------------------------------------------- static bool acquireWrite(AtomicInt &key, AtomicInt &state) { int tmp = (state |= STATE_WRITE); if (tmp == STATE_READ_AND_WRITE) { state = STATE_READ; return false; } bool tmp2 = (++key == 0); key = 0; return tmp2; } //------------------------------------------------------------------------- static void releaseWrite(AtomicInt &key, AtomicInt &state) { state = STATE_FREE; key = -1; } //------------------------------------------------------------------------- static bool acquireRead(AtomicInt &counter, AtomicInt &state) { int tmp = (state |= STATE_READ); if (tmp == STATE_READ) { counter++; return true; } state = STATE_WRITE; return false; } //------------------------------------------------------------------------- static void releaseRead(AtomicInt &counter, AtomicInt &state) { if (--counter == 0) state = STATE_FREE; } //------------------------------------------------------------------------- }; #endif // SPINLOCK_H
Вот так)
|
|
|
1687
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 29, 2011, 18:07
|
Никак не даёт покоя идея реализации этого злосчастного SpinLock. Несколько пересмотрел логику и теперь это выглядит так: C++ (Qt) #ifndef SPINLOCK_H #define SPINLOCK_H #include <assert.h> #include <cstdatomic> template <class T> class SpinLock { public: typedef atomic<int> AtomicInt; SpinLock() : m_key(-1), m_state(STATE_FREE) {} void setData(const T &data) { while (!acquireWrite(m_key, m_state)); m_data = data; releaseWrite(m_key, m_state); } T getData() const { while (!acquireRead(m_key, m_state)); T data = m_data; releaseRead(m_key, m_state); return data; } private: enum { STATE_FREE = 0, STATE_READ = 1, STATE_WRITE = 2, STATE_READ_AND_WRITE = 3 }; AtomicInt m_key; AtomicInt m_state; T m_data; //------------------------------------------------------------------------- static bool acquireWrite(AtomicInt &key, AtomicInt &state) { int tmp = (state |= STATE_WRITE); if (tmp == STATE_READ_AND_WRITE) { state = STATE_READ; return false; } bool tmp2 = (++key == 0); key = 0; return tmp2; } //------------------------------------------------------------------------- static void releaseWrite(AtomicInt &key, AtomicInt &state) { state = STATE_FREE; key = -1; } //------------------------------------------------------------------------- static bool acquireRead(AtomicInt &/*key*/, AtomicInt &state) { int tmp = (state |= STATE_READ); if (tmp == STATE_READ) return true; state = STATE_WRITE; return false; } //------------------------------------------------------------------------- static void releaseRead(AtomicInt &/*key*/, AtomicInt &state) { state = STATE_FREE; } //------------------------------------------------------------------------- }; #endif // SPINLOCK_H Краткое описание: Есть две переменные key и state. state может принимать следующие значения: 00 = 0 - STATE_FREE; 01 = 1 - STATE_READ; 10 = 2 - STATE_WRITE; 11 = 3 - STATE_READ_AND_WRITE; (виртуальное состояние) key нужин лишь для того, чтобы отобрать какой либо один поток для записи. Как Вам такой вариант?
|
|
|
1689
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записl
|
: Март 29, 2011, 11:10
|
Если удасца реализовать первоночальный SpinLock, то его можно будет ускорить создав буфер даных. Тогда простои при записи и чтении можно будет существенно сократить.
Не понял, расскажите подробнее В смысле, можно создать внутренний буфер (массив тобиш). Пока читатели читают из одной ячейки массива, писатель, чтоб не простаивать пишет в следующую, и после того как запишет устанавливает индекс для чтения с актуальными данными. При этом в следующий раз писатель будет выбирать новый индекс для записи с учётом того, откуда читают данные читатели. Короче, читатели всегда будут знать где актуальные даные, а писатели будут знать куда писать, чтобы не было простоев. Мне проще код накидать)) Но это когда разберусь со SpinLock ом.
|
|
|
1691
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 29, 2011, 10:20
|
Хм.. Ещё один вариант, надеюсь последний) C++ (Qt) #ifndef SPINLOCK_H #define SPINLOCK_H #include <assert.h> #include <cstdatomic> template <class T> class SpinLock { public: typedef atomic<int> AtomicInt; SpinLock() : m_count_readers(0), m_state_write(-1) {} void setData(const T &data) { while (!acquireWrite(m_count_readers, m_state_write)); m_data = data; releaseWrite(m_state_write); } T getData() const { while (!acquireRead(m_count_readers, m_state_write)); T data = m_data; releaseRead(m_count_readers, m_state_write); return data; } private: AtomicInt m_count_readers; AtomicInt m_state_write; T m_data; static bool acquireWrite(AtomicInt &readers, AtomicInt &state_write) { bool tmp = (++state_write == 0); state_write = 0; return (tmp && !readers); } //------------------------------------------------------------------------- static void releaseWrite(AtomicInt &state_write) { state_write = -1; } //------------------------------------------------------------------------- static bool acquireRead(AtomicInt &readers, AtomicInt &state_write) { readers++; bool tmp = (++state_write == 0); state_write = 0; if (tmp) { state_write = -1; return true; } readers--; return false; } //------------------------------------------------------------------------- static void releaseRead(AtomicInt &readers, AtomicInt &state_write) { if(--readers == 0) state_write = -1; } //------------------------------------------------------------------------- }; #endif // SPINLOCK_H
Во всяком случае, ситуация описанная brankovic: R0 читал R1 дошёл до if, увидел, что кто-то читает, отправился на else R1 заснул R0 ушёл (readers == 0) W пришёл и захватил на запись R1 проснулся, сделал ++readers и вернул true
теперь R0 читает, а W пишет
исключена.
|
|
|
1692
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 28, 2011, 20:23
|
Короче, окончательный вариант SpinLock: C++ (Qt) #ifndef SPINLOCK_H #define SPINLOCK_H #include <assert.h> #include <cstdatomic> template <class T> class SpinLock { public: typedef atomic<int> AtomicInt; SpinLock() : m_count_readers(0), m_state_write(-1) {} void setData(const T &data) { while (!acquireWrite(m_count_readers, m_state_write)); m_data = data; releaseWrite(m_state_write); } T getData() const { while (!acquireRead(m_count_readers, m_state_write)); T data = m_data; releaseRead(m_count_readers, m_state_write); return data; } private: AtomicInt m_count_readers; AtomicInt m_state_write; T m_data; static bool acquireWrite(AtomicInt &readers, AtomicInt &state_write) { bool tmp = (++state_write == 0); state_write = 0; return (tmp && !readers); } //------------------------------------------------------------------------- static void releaseWrite(AtomicInt &state_write) { state_write = -1; } //------------------------------------------------------------------------- static bool acquireRead(AtomicInt &readers, AtomicInt &state_write) { if (!readers) { int tmp = (++state_write == 0); readers = tmp; } else readers++; return readers; } //------------------------------------------------------------------------- static void releaseRead(AtomicInt &readers, AtomicInt &state_write) { if (--readers == 0) state_write = -1; } //------------------------------------------------------------------------- }; #endif // SPINLOCK_H
|
|
|
1693
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записl
|
: Март 28, 2011, 20:09
|
это как-то уж слишком коварно.. По смыслу m_ax спинлок хотел, а в спинлоке так и должно быть.
Не должно. Про свой OSX могу сказать: OSSpinLockLock (спиннер) не просто "крутит while". А TBB сначала просто делает какое-то число nop, затем вставляет pause а потом уж shed_yield. При этом учитывается специфика процессора. В общем, это на порядок сложнее чем то что сейчас обсуждается А без этого (увы) могут быть заметные тормоза для др. задач Если удасца реализовать первоночальный SpinLock, то его можно будет ускорить создав буфер даных. Тогда простои при записи и чтении можно будет существенно сократить.
|
|
|
1695
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 28, 2011, 19:12
|
C++ (Qt) if (!readers) readers = (state_write == -1);
Те же грабли. Последняя строка не атомарна. С Ларисой я действительно пил вино, но это было на пасху
Т.е. state_write действительно был -1, но до тех пор пока это присвоится readers, др. нитка успеет захватить по записи. Лучше держать все в одном AtomicInt. с двумя переменными заморочек намного больше. Ваше упорство избежать CAS "заслуживает лучшего применения" И как там у Вас с холостым ходом? [/quote] Не атомарно? Ну это можно исправить следующим образом: C++ (Qt) static bool acquireRead(AtomicInt &readers, AtomicInt &state_write) { if (!readers) readers = 1; // атомарно int tmp = (state_write == -1); // атомарно readers = tmp; // атомарно else readers++; return readers; }
И как там у Вас с холостым ходом?
В смысле? С ним что-то не так?
|
|
|
|
|