Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Потоки и общие данные / 25 сообщений из 26, страница 1 из 2
09.04.2019, 23:54
    #39799065
sergq
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
Здравствуйте

Раньше не работал с потоками и общими данными. Голова квадратная, по этому могу тупить.

Упрощенно. Допустим есть некий буфер. Размером килобайт. В начале его примерно такая структура.

theader = record
LockFlag: byte;
ReaderCount: integer;
End

Последняя переменная—счетчик читателей. Исходя из того, что писать в буфер можно при наличии нуля читателей.

Как я вижу работу потоков с такого рода буфером. С критическими секциями.

Допустим изначально флаг блокировки none. Ну вернее флаг текущей выполняемой операции с блоком.

Первый поток хочет читать. В критической секции проверяет флаг блокировки. Если none или blockread, то открываем критическую секцию, ставим флаг блокировки в blockread, увеличиваем счетчик читателей и выходим из критической секции. Далее читаем—зачитываемся. По окончании чтения опять открываем критическую секцию, уменьшаем количество читателей и если получилось ноль— ставим блокировку none. Иначе оставляем блокировку на чтение.И завершаем критическую секцию.

А если хотим писать туда, то при отсутствии блокировки ставим блокировку типа blockwrite. Выходим из критической, пишем. Потом опять в критической сбрасываем блокировку в none.

Пока у буфера блокировка типа blockread и readercounter не равен нулю ставить блокировку на запись не можем. Те если поток хочет осуществить запись он должен подождать , пока блокировка будет none. Единственный вопрос— а как подождать? И соответственно желающие записать должны подождать, пока блокировка не станет none. Но как? Простым циклом while? Подозреваю, что не прокатит

Мысль вроде в правильном направлении, но не уверен, тк ни разу с потоками и разделяемыми буферами не работал
...
Рейтинг: 0 / 0
10.04.2019, 00:07
    #39799073
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
sergqЕдинственный вопрос— а как подождать?Самый простой и скорее всего лучший вариант - выкинуть все эти флаги и счетчики, а все действия с буфером производить внутри "открытой" критической секции, и тогда всё будет работать само.
...
Рейтинг: 0 / 0
10.04.2019, 00:14
    #39799077
sergq
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
YuRocksergqЕдинственный вопрос— а как подождать?Самый простой и скорее всего лучший вариант - выкинуть все эти флаги и счетчики, а все действия с буфером производить внутри "открытой" критической секции, и тогда всё будет работать само.

И об этом думал) собственно да, читателей может быть много. И они двумя критическими секциями ставятт—снимают флаги и счетчики.

А ждать то для блокировки на запись вроде как надо. Ибо допустим есть 20 читателей. И тут вдруг нарисовался писатель. А он может писать только тогда, когда readercount равен нулю. Те в любом случае ему надо как то ждать, когда отвалятся все 20 читателей
...
Рейтинг: 0 / 0
10.04.2019, 00:19
    #39799079
asviridenkov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
...
Рейтинг: 0 / 0
10.04.2019, 00:25
    #39799081
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
sergqписать в буфер можно при наличии нуля читателей.

А если нужен тебе RWLock, так и используй его. Он в VCL уже готовый есть.

Но я бы ещё поразмыслил над задачей. Может, для пущей производительности, Copy-on-Write
там будет в жилу.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
10.04.2019, 01:02
    #39799089
sergq
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
Dimitry Sibiryakovsergqписать в буфер можно при наличии нуля читателей.

А если нужен тебе RWLock, так и используй его. Он в VCL уже готовый есть.

Но я бы ещё поразмыслил над задачей. Может, для пущей производительности, Copy-on-Write
там будет в жилу.


Да вроде как я и описал RWlock. Только не осилил задачу ожидания возможности записи. Хочется все ж без vcl, самому) мозги поформировать. А то последнее время разжижаются

copy-on-write это когда при необходимости записи мы делаем копию буфера, модифицируем и помещаем обратно? Чет не пойму зачем так делать? Все равно ж в итоге придется вернуть модифицированные данные на место. А для этого как раз и придется ставить лок при записи обратно. Чтоб читатели не ломанулись читать недописанные данные
...
Рейтинг: 0 / 0
10.04.2019, 01:10
    #39799090
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
sergq Хочется все ж без vcl, самому) мозги поформировать. А то последнее время
разжижаются
Тогда тебе к Рихтеру, там расписаны эти примитивы. Одним мутексом не обойдёшься, надо два.

sergqcopy-on-write это когда при необходимости записи мы делаем копию буфера, модифицируем и
помещаем обратно? Чет не пойму зачем так делать? Все равно ж в итоге придется вернуть
модифицированные данные на место. А для этого как раз и придется ставить лок при записи
обратно. Чтоб читатели не ломанулись читать недописанные данные

"Запись обратно" это атомарная операция присваивания одного указателя, в локе не
нуждается. Старые читатели читают старый буфер, новые читатели читают уже новый. Старый
освобождается при падении его счётчика ссылок до нуля.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
10.04.2019, 01:18
    #39799092
sergq
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
Dimitry Sibiryakovsergq Хочется все ж без vcl, самому) мозги поформировать. А то последнее время
разжижаются
Тогда тебе к Рихтеру, там расписаны эти примитивы. Одним мутексом не обойдёшься, надо два.

sergqcopy-on-write это когда при необходимости записи мы делаем копию буфера, модифицируем и
помещаем обратно? Чет не пойму зачем так делать? Все равно ж в итоге придется вернуть
модифицированные данные на место. А для этого как раз и придется ставить лок при записи
обратно. Чтоб читатели не ломанулись читать недописанные данные

"Запись обратно" это атомарная операция присваивания одного указателя, в локе не
нуждается. Старые читатели читают старый буфер, новые читатели читают уже новый. Старый
освобождается при падении его счётчика ссылок до нуля.


Нездоровое желание читать и писать в один и тот же участок памяти)

Можно ж теоретически через мьютекс подождать? Первый читатель со создаст мьютекс. Последний освободит. А писатель будет ждать освобождения?

И кстати почему запись обратно не нуждается в локе? Операция присваивания то тоже не атомарна? Тк все ж состоит из несколько асм комманд? Только через interlocked функции. А это тоже своего рода лок. Где то тут даже вы про это писали.
...
Рейтинг: 0 / 0
10.04.2019, 12:38
    #39799264
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
sergqОперация присваивания то тоже не атомарна? Тк все ж состоит из несколько асм комманд?
Только через interlocked функции. А это тоже своего рода лок.

interlocked функции, конечно, лок, но такой короткий, что на производительности
практически не сказывается. И при желании можно обойтись и без них, поскольку на
интеловской архитектуре для данных до размера указателя нет thorn reads, простое
присваивание и чтение атомарны, это одна asm команда.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
10.04.2019, 12:58
    #39799279
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
Dimitry Sibiryakovпростое
присваивание и чтение атомарны, это одна asm команда
Только простое присваивание и чтение.
А еще всё это так в рамках одного процессора (ядра).
...
Рейтинг: 0 / 0
10.04.2019, 13:01
    #39799283
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
YuRockА еще всё это так в рамках одного процессора (ядра).В смысле, что может произойти одновременное выполнение этой одной asm-команды несколькими процессорами.
...
Рейтинг: 0 / 0
10.04.2019, 13:06
    #39799289
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
YuRockТолько простое присваивание и чтение.

Только о них я и говорю.

YuRockВ смысле, что может произойти одновременное выполнение этой одной asm-команды несколькими
процессорами.

Может, но память-то у них всё равно общая, так что один процессор не сможет прочитать
смесь из двух разных значений, записываемых остальными. То есть если в памяти лежит
12345678, а один процессор пишет 87654321, то второй никогда не получит 12344321 или
87655678. По крайней мере именно так я читаю спеки.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
10.04.2019, 13:17
    #39799300
Dimonka
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
Есть ещё стандартный TMultiReadExclusiveWriteSynchronizer и его альтернативы:
https://stackoverflow.com/questions/10378253/faster-tmultireadexclusivewritesynchronizer
...
Рейтинг: 0 / 0
10.04.2019, 13:21
    #39799305
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
Dimitry Sibiryakovодин процессор не сможет прочитать смесь из двух разных значений, записываемых остальными.

Хотя, конечно, это только для очень узкого класса задач. COW так не организуешь: пока ты
прочитал старый указатель и пытаешься увеличить счётчик ссылок, кто-то другой его уже
довёл до нуля, память освободил и ты получаешь AV.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
10.04.2019, 13:59
    #39799343
Valery_B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
sergqМысль вроде в правильном направлении, но не уверен, тк ни разу с потоками и разделяемыми буферами не работал

У тебя не правильный подход.
Для чтения - не нужны блокировки и критические секции.
Как правильно сделать чтение/запись из общих данных написано в шаблоне проектирования Блокировка с двойной проверкой

Краткий смысл:
1. Прежде чем прочитать, спрашиваем - А есть ли то, что нужно прочитать?
2. Если есть - читаем. В общем-то и всё.
3. Если нет, входим в крит. секцию.
4. После входа - проверяем повторно А есть ли то, что нужно прочитать? , т.к. во время входа туда мог положить это другой поток.
5. Если данные есть - читаем. При необходимости - кладём их. Выходим из критической секции.
...
Рейтинг: 0 / 0
10.04.2019, 15:38
    #39799448
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
Dimitry SibiryakovYuRockТолько простое присваивание и чтение.

Только о них я и говорю.

YuRockВ смысле, что может произойти одновременное выполнение этой одной asm-команды несколькими
процессорами.

Может, но память-то у них всё равно общая, так что один процессор не сможет прочитать
смесь из двух разных значений, записываемых остальными. То есть если в памяти лежит
12345678, а один процессор пишет 87654321, то второй никогда не получит 12344321 или
87655678. По крайней мере именно так я читаю спеки.
Это всё понятно. Только когда, скажем, 8 процессоров одновременно захотят увеличить данные, находящиеся в ячейке памяти, на единицу, то в результате мы можем получить, что это значение увеличилось на [от 1 до 8 как повезет]. Хотя и запись, и чтение - атомарные операции, но толку от этого будет мало.
...
Рейтинг: 0 / 0
10.04.2019, 16:02
    #39799485
X-Cite
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
YuRock,

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

https://habr.com/ru/post/196548/
...
Рейтинг: 0 / 0
10.04.2019, 16:35
    #39799541
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
YuRock8 процессоров одновременно захотят увеличить данные

А, так "увеличить" это совсем другая операция, я про неё не говорил.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
10.04.2019, 17:53
    #39799593
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
X-CiteОни будут синхронизированы между собойЕсли это сделать.
...
Рейтинг: 0 / 0
10.04.2019, 17:55
    #39799594
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
Dimitry SibiryakovYuRock8 процессоров одновременно захотят увеличить данные

А, так "увеличить" это совсем другая операция, я про неё не говорил.Да, не говорил. Но просто что-то прочитать и просто что-то записать - обычно, этого мало для построения логики программ :)
А малейшее усложнение уже ведет к необходимости синхронизации группы команд.
...
Рейтинг: 0 / 0
10.04.2019, 18:13
    #39799600
Barlone
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
Dimitry SibiryakovDimitry Sibiryakovодин процессор не сможет прочитать смесь из двух разных значений, записываемых остальными.

Хотя, конечно, это только для очень узкого класса задач. COW так не организуешь: пока ты
прочитал старый указатель и пытаешься увеличить счётчик ссылок, кто-то другой его уже
довёл до нуля, память освободил и ты получаешь AV.
Да, ну очень узкого. Даже классика - Алгоритм Деккера на современных процессорах просто так без барьеров памяти не будет работать корректно.
...
Рейтинг: 0 / 0
11.04.2019, 09:55
    #39799737
kealon(Ruslan)
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
sergqДа вроде как я и описал RWlock. Только не осилил задачу ожидания возможности записи. Хочется все ж без vcl, самому) мозги поформировать. А то последнее время разжижаются
самый примитивный и рабочий вариант- тынц
единственное практически у всех алгоритмов нельзя с readlock-a войти во writelock
...
Рейтинг: 0 / 0
11.04.2019, 10:06
    #39799742
kealon(Ruslan)
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
Dimitry SibiryakovМожет, но память-то у них всё равно общая, так что один процессор не сможет прочитать
смесь из двух разных значений, записываемых остальными. То есть если в памяти лежит
12345678, а один процессор пишет 87654321, то второй никогда не получит 12344321 или
87655678. По крайней мере именно так я читаю спеки.
всё это работает когда данные выровнены + не превышают 32-бит, с границами блоков всё уже не так просто, байты могут быть вообще в разных физических планках памяти

Dimitry Sibiryakovinterlocked функции, конечно, лок, но такой короткий, что на производительности
практически не сказывается. они заставляют конкурирующий проц сбрасывать кэш

для теста достаточно запустить несколько потоков только и делающих что изменяющих один Int, если это будет делать только один поток, то это будет раз в 5 быстрее, чем если бы это делали несколько, но это конечно такой критический вариант теста
...
Рейтинг: 0 / 0
11.04.2019, 16:19
    #39800043
Bred eFeM
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
sergq,
Код: pascal
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.
TSRWSynLock = record
private
 nRd, nWr    :Integer;
 class procedure Sleep_; static;
 // class var fSleepCount : NativeInt;
 class var fNoSleepInWait :Boolean;
public
 class property NoSleepInWait : Boolean read fNoSleepInWait write fNoSleepInWait;
public
 procedure RdEnter;
 procedure WrEnter;
 procedure RdLeave; inline;
 procedure WrLeave; inline;
end;

procedure TSRWSynLock.RdEnter;
begin
 while True do
  begin
   AtomicIncrement(nRd);
   if (nWr = 0)
    then Break;

   AtomicDecrement(nRd);
   Sleep_;
  end;
end;

procedure TSRWSynLock.WrEnter;
begin
 while True do
  begin
   if ( AtomicExchange(nWr, 1) = 1 )
    then Sleep_
   else
    begin
     while (nRd <> 0) do Sleep_;
     Break;
    end;
  end;
end;

procedure TSRWSynLock.RdLeave;
begin
 AtomicDecrement(nRd);
end;

class procedure TSRWSynLock.Sleep_;
begin
 // AtomicIncrement(fSleepCount);
 if ( fNoSleepInWait )
  then YieldProcessor()
   else if ( not SwitchToThread() )
    then Sleep(1);
end;

procedure TSRWSynLock.WrLeave;
begin
 AtomicDecrement(nWr);
end;

...
Рейтинг: 0 / 0
14.04.2019, 16:17
    #39801078
sergq
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Потоки и общие данные
Бум осмысливать всю информацию)


Возник такой вопрос. По логике блокировок. Допустим применительно к базам данных.

Допустим есть операция вставки. В моем понимании она происходит так. Может слегка упрощенно.
1. Поиск последней pointer page
2. На ней поиск свободного слота.
3. По номеру из слота взять data page
4. Найти на ней свободный слот для данных.
5. Записать инфу , что очередной слот занят.
6. Записать сами данные

Те в операции участвуют два разделяемых блока данных. Но вроде операция над ними логически (транзакционно) едина. И может случиться так, что один тред ну задумался. Между вторым и третьим шагом. А второй тред шустро выполнил все шесть шагов.
В итоге у первого треда после раздумий есть номер слота на PP с его точки зрения свободный, но на самом деле уже занят.

Как в таких случаях блокировки расставляются?
Как вариант.
Блокируется PP, найденная на первом шаге для всех операций, кроме выборки. И так же с DP. И вся эта связка разблокируется только после того, как все последовательность операций выполнится?
...
Рейтинг: 0 / 0
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Потоки и общие данные / 25 сообщений из 26, страница 1 из 2
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]