Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 2005.07.11;
Скачать: [xml.tar.bz2];

Вниз

Продолжение дискуссии "Как избежать гонок в потоках"   Найти похожие ветки 

 
evvcom ©   (2005-06-02 17:04) [0]

Продолжение трепа http://delphimaster.net/view/1-1117521494/ переносим сюда.

Я не стал все очень подробно расписывать, потому, наверное, и не был понят. Получил в ответ придирки к словам. По порядку.

Проще начать с

> Alexander Panov ©   (02.06.05 15:41) [81]
> 3. С какого перепугу поток вдруг начнет обрабатывать PostMessage?

Согласен. Упустил. Сам поток как таковой не обрабатывает оконные сообщения. Для этого проще извещать основной поток (любую TForm).
Далее...


 
Игорь Шевченко ©   (2005-06-02 17:22) [1]


> Сам поток как таковой не обрабатывает оконные сообщения


Обрабатывает. Собственно, все сообщения окнам потока потоку и приходят. Но лучше использовать PostThreadMessage и проверять равенство HWND=0


 
Alexander Panov ©   (2005-06-02 18:32) [2]

Игорь Шевченко ©   (02.06.05 17:22) [1]
evvcom ©   (02.06.05 17:04)

Изначально поставлена такая задача:

1. Есть заранее неопределенное число потоков, которые получают данные извне, которые заполняющие некий буфер. Назовем их писателями(W)

2. Есть один или несколько потоков-читателей, которые в неопределенный момент времени должны прочитать из буфера данные. Для простоты пусть таковой будет один(R).

3. В некий момент времени R получает команду прочитать буфер.
 При этом все остальные потоки W должны немедленно прекратить свою работу по записи буфера, кроме потока, который в данный момент заполняет буфер.
 Поток, который пишет в буфер в данный момент, должен записать текущую порцию данных,а затем также должен  прекратить попытки записывать данные до тех пор, пока буфер не будет прочитан R.

Предполагаемые требования к потокам:

1. Не должно быть холостых циклов(while Flag<>Value do)
2. Процесс не должен влиять на работу остальных приложений в системе.
---------------------------------

Утверждается:

Для выполнения поставленной задачидостаточно использования критических секций. Другие объекты синхронизации(Mutex, Event, и подобные) использовать необходимости нет.

Предлагаю решить поставленную задачу с этими ограничениями.
-----------------------------------------------------------

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


 
iZEN ©   (2005-06-02 19:55) [3]

Alexander Panov,

http://izen.dev.juga.ru/notebook/solutions/threads/prodcons.html

ОНО?


 
Alexander Panov ©   (2005-06-02 20:09) [4]

iZEN ©   (02.06.05 19:55) [3]
ОНО?


Не совсем.
Потребитель должен иметь приоритет преде производителем.


 
GrayFace ©   (2005-06-02 21:40) [5]

А преоритет точно действует на крит. секции?

evvcom ©   (01.06.05 11:16) [35]
Ну и нафига здесь synchronize до выхода из крит.секции, да еще try finally навесил?

Чтоб главному потоку не приходилось ждать, пока какией-то потоки проводят запись в массив.

leonidus
См. книгу Рихтера "Windows для профессионалов". По слухам, есть на http://www.podgoretsky.com

leonidus ©   (01.06.05 19:42) [57]
Это да, но я так понял, что кто первый тот и молодец.

Как я помню, не всегда.

Alexander Panov ©   (01.06.05 21:01) [59]
Для таких флагов подходят как семафоры, так и мьютексы.

Мьютексы-флажки?? По-моему, на роль флажка лучше всего подходит Event(Manual Reset).

Digitman ©   (02.06.05 9:45) [68]
Основной поток имеет бонус в приоретете, т.к. имеет окна. Но это можно отключить где-то в настройках системы.

evvcom ©   (02.06.05 13:54) [79]
Во время оповещения читающий поток ждет, когда ему ОС выделит квант времени. :)

А в это время кто-то захватывает критическую секцию...

Alexander Panov
Чем использование объектов синхронизации будет лучше, чем вариант с FCS.Enter; Synchronize; FCS.Leave?


 
Eraser ©   (2005-06-02 21:50) [6]

Alexander Panov ©   (02.06.05 18:32) [2]

При этом все остальные потоки W должны немедленно прекратить свою работу по записи буфера, кроме потока, который в данный момент заполняет буфер.


Непонял, а что в ОДИН И ТОТ ЖЕ буффер пишут сразу одновременно несколько потоков? Это как?


 
Igorek ©   (2005-06-02 22:12) [7]

Alexander Panov ©   (02.06.05 18:32) [2]
Основной поток создает две крит. секции. Назовем их S1 и S2.
Читатель перед чтением ждет S2.
Писатель перед записью ждет S1, потом S2.


 
Alexander Panov ©   (2005-06-02 23:20) [8]

Igorek ©   (02.06.05 22:12) [7]

Более развернутый алгоритм можно? Либо код.


 
Alexander Panov ©   (2005-06-02 23:23) [9]

Хочу заметить, что стандартный код из книги, пример о "потребителях и производителях" не подойдет.
Не подойдет из-за условия 3.
Поэтому стандартный код прошу не предлагать. В нем другая ситуация. Кардинально.


 
Eraser ©   (2005-06-02 23:23) [10]

Вот несколько типовых схем управления потоками, не вкурсе то это или не то, т.к. ветку http://delphimaster.net/view/1-1117521494/ не читал.
Но думаю в любом случае кому нибудь инфа пригодится.

http://mbo88.narod.ru/ToC.html


 
Alexander Panov ©   (2005-06-02 23:27) [11]

Кстати, решение не так просто, как это описывали в ветке evvcom и Игорь Шевченко.


 
КаПиБаРа ©   (2005-06-03 06:16) [12]

Igorek ©   (02.06.05 22:12) [7]
Согласен. Я предлагал такой же алгоритм, только на мьютексах.


 
Тульский ©   (2005-06-03 08:17) [13]


> См. книгу Рихтера "Windows для профессионалов". По слухам,
> есть на http://www.podgoretsky.com

Не вижу


 
leonidus ©   (2005-06-03 08:19) [14]

Коль уж я эту кашу заварил, хочу кое что уточнить:

>Alexander Panov ©   (02.06.05 18:32) [2]
Изначально поставлена такая задача:

1. Есть заранее неопределенное число потоков, которые получают данные извне, которые заполняющие некий буфер. Назовем их писателями(W)

Тут один момент. Эти потоки - парсеры, и в буфер они будут заносить результаты своей работы (парсинга) т.е. нашли ссылку прошли по бцферу если такой же не нашли то добавляем ссылку и продолжаем парсинг. Сама запись конечно будет происходить быстро, но вот парсинг может и затянуться (странички разные по объему бывают).

2. Есть один или несколько потоков-читателей, которые в неопределенный момент времени должны прочитать из буфера данные. Для простоты пусть таковой будет один(R).
Читает буфер однозначно один поток - основной, он же передает считанные даные в блок закачки.

3. В некий момент времени R получает команду прочитать буфер.
При этом все остальные потоки W должны немедленно прекратить свою работу по записи буфера, кроме потока, который в данный момент заполняет буфер.
Поток, который пишет в буфер в данный момент, должен записать текущую порцию данных,а затем также должен  прекратить попытки записывать данные до тех пор, пока буфер не будет прочитан R.

Да это так, только я бы сформулировал так:
3. В некий момент времени R получает команду прочитать буфер.
При этом если в буфер производилась запись потоком W, нужно что бы он доработал до конца, и передал буфер потоку R.

А то потом позникают вопросы типа "что в буфер пишут одновременно несколько потоков ?". Пишет или читает ВСЕГДА один поток, для этого я и применил критическую секцию

Поток, который пишет в буфер в данный момент, должен записать текущую порцию данных,а затем также должен  прекратить попытки записывать данные до тех пор, пока буфер не будет прочитан R.

Пишущий поток должен ОБЯЗАТЕЛЬНО доработать до конца и умереть передав управление потоку R. И больше никаких попыток записиименно он делать не должен.

Утверждается:

Для выполнения поставленной задачи достаточно использования критических секций. Другие объекты синхронизации(Mutex, Event, и подобные) использовать необходимости нет.


Это было бы идеально. Т.к. во-первых я еще и с критическими секциями до конца не разобрался что бы лезть в еще более густые дебри синхронизации потоков. Меня бы вполне устроила бы схема изменения приоритетов или несложный алгоритм взведения флагов.


 
evvcom ©   (2005-06-03 08:33) [15]


> Alexander Panov ©   (02.06.05 18:32) [2]

Изначально была поставлена задача, удовлетворяющая 1 и 2 пункту. В последствии что-то было намекающее на 3, но явно не прозвучавшее, во всяком случае я не заметил. С учетом 3 пункта, я согласен, что одних крит.секций недостаточно.
И, Александр, видите, что и довольно опытные и знающие программисты в своих рассуждениях иногда упускают какой-то момент, из-за чего некоторые стремятся зацепиться за эту промашку. Доказательством тому служит это:

> Eraser ©   (02.06.05 21:50) [6]
> Alexander Panov ©   (02.06.05 18:32) [2]
>
> При этом все остальные потоки W должны немедленно прекратить
> свою работу по записи буфера, кроме потока, который в данный
> момент заполняет буфер.
>
> Непонял, а что в ОДИН И ТОТ ЖЕ буффер пишут сразу одновременно
> несколько потоков? Это как?

Хотя мне понятно, что Вы имели в виду.


> Игорь Шевченко ©   (02.06.05 17:22) [1]
>
> > Сам поток как таковой не обрабатывает оконные сообщения
>
>
> Обрабатывает. Собственно, все сообщения окнам потока потоку
> и приходят.

Каюсь, Игорь, упустил причастный оборот: "поток, не создавший окна для обработки оконных сообщений". Может и сейчас не совсем правильно построил фразу, так что сильно не пинайте.


 
leonidus ©   (2005-06-03 08:38) [16]

>evvcom ©   (03.06.05 08:33) [15]
С учетом 3 пункта, я согласен, что одних крит.секций недостаточно.

Ну а изменения приоритетов между W и R тоже не достаточно?


 
Digitman ©   (2005-06-03 08:45) [17]


> А то потом позникают вопросы типа "что в буфер пишут одновременно
> несколько потоков ?"


а чтобы вопросов такого типа не возникало, всегда нужно уточнять, что из себя представляет буфер, ибо буфер - термин абстрактный

к примеру, есть buf: array[0..2] of byte (чем не буфер) ?

поток 0 пишет в buf[0]
поток 1 пишет в buf[1]
поток 2 читает из buf[2]

чем не "одновременность" ? никто никому не мешает ..


 
КаПиБаРа ©   (2005-06-03 08:45) [18]

evvcom ©   (03.06.05 8:33) [15]
С учетом 3 пункта, я согласен, что одних крит.секций недостаточно.


Я считаю что достаточно и Igorek ©  в (02.06.05 22:12) [7] показал как это сделать.


 
evvcom ©   (2005-06-03 08:54) [19]


> Ну а изменения приоритетов между W и R тоже не достаточно?

В смысле, если изменить приоритет потока? Т.е. ситуация описанная Александром:
1. 100 потоков W с приоритетом Normal-1 выполнили EnterCriticalSection и стопорнулись (ну кроме первого)
2. Потом поток R c приоритетом Normal тоже выполнил EnterCriticalSection и тоже стопорнулся.
3. 1-ый поток W освободил CriticalSection

Вопрос, какой из потоков войдет в крит. секцию один из W или R?

Если честно, я таких экспериментов не проводил, в литературе задачи такой не встречал, но можно ж попробовать. Но если приоритеты одинаковы, то я встречал следующее, что нигде не задокументирована очередность получения крит.секции потоками в этом случае, хотя настоящая реализация этих механизмов в ОС дает доступ к секции тому потоку, который раньше его запросил. Однако, делать ставку на то, что такой механизм останется и в будущем, делать нельзя, т.к. это не задокументировано!

Жди, может еще кто чего добавит.


 
evvcom ©   (2005-06-03 09:03) [20]


> Я считаю что достаточно и Igorek ©  в (02.06.05 22:12) [7]
> показал как это сделать.


> Читатель перед чтением ждет S2.
> Писатель перед записью ждет S1, потом S2.

А такой вариант:
1. W1 входит в S1 и S2
2. W2 запрос S1, ожидание
3. R запрос S2, ожидание
4. W1 выходит из S2 и S1
5. W2 входит в S1 и S2
А R так и продолжает ждать


 
КаПиБаРа ©   (2005-06-03 09:11) [21]

Разрешите вас поправить

>А такой вариант:
>1. W1 входит в S1 и S2
>2. W2 запрос S1, ожидание
>3. R запрос S2, ожидание
>4. W1 выходит из S2 и S1
>5. W2 входит в S1 и S2
> А R так и продолжает ждать


А такой вариант:
1. W1 входит в S1 и S2
2. W2 запрос S1, ожидание
3. R запрос S2, ожидание
4. W1 выходит из S2
5  R входит в S2
6  W1 выходит из S1 и продолжает парсить
7. W2 входит в S1 и ждет S2
8  R выходит из S2
9  W2 входит в S2 и т.д.

Суть в том что S1 пропускает пишушие потоки только по одному.
К S2 одновременно могут подойти 1 пишущий поток и 1 читающий.
Если пишущий проскочит вперед, то следующим за ним обязательно будет читающий


 
evvcom ©   (2005-06-03 09:20) [22]


> 4. W1 выходит из S2
> 5  R входит в S2

А где написано, что при выходе W1 из S2, ОС отберет тут же у W1 его квоту и передаст R? Я соглашусь с этим теоретически только, если приоритет потоков W будет ниже R, в противном случае нет. И окончательно соглашусь только после экспериментов (или опровергну).


 
КаПиБаРа ©   (2005-06-03 09:38) [23]

evvcom ©   (03.06.05 9:20) [22]
А где написано, что при выходе W1 из S2, ОС отберет тут же у W1 его квоту и передаст R?


Интересный вопрос. Надо экспериментировать.


 
evvcom ©   (2005-06-03 09:57) [24]

Это все очень интересно. Но изначально была описана leonidus задача такова, что условие 3 Alexander Panov ©   (02.06.05 18:32) [2] имхо на нее можно не накладывать. Тогда всё упрощается. Объясните мне зачем это условие здесь соблюдать, если каждый из потоков занимает секцию на миллионные, если не меньше, доли секунды? И зачем потоку R надо немедленно начинать парсить, если через долю секунды он все равно остановится и станет ждать, пока поток W докачает из интернета очередную порцию данных?


 
КаПиБаРа ©   (2005-06-03 10:05) [25]

evvcom ©   (03.06.05 9:57) [24]
Может быт это не столь существенно но у вас несколько искаженное понимание первоначальной задачи.

Парсингом занимается не R поток, а W потоки. Закачивает страницы какой-то "блок закачки". R поток читает данные из буфера для передачи в "блок закачки". см leonidus ©   (03.06.05 8:19) [14]


 
Igorek ©   (2005-06-03 10:34) [26]

Alexander Panov ©   (02.06.05 23:20) [8]
Более развернутый алгоритм можно? Либо код.

Код нету времени писать и тестировать. А алгоритм вроде прозрачный.

evvcom ©   (03.06.05 9:20) [22]
А где написано, что при выходе W1 из S2, ОС отберет тут же у W1 его квоту и передаст R?

Ты же сам написал - в настоящей реализации ОС запросы на крит. секции идут fifo для потоков одинаковых приоритетов. Поскольку я не писал о приоритетах, то они одинаковы по умолчанию. В качестве гарантии (кромы разности приоритетов читателя и писателя) также можно добавить между вызовами писателем S1 и S2 (и/или между освобождением в обратном порядке) задержку, гарантировано превышающую макс. таймаут на запрос проц. времени (или гарантированую квоту//уж не помню, надо в Рихтере почитать). Также можно в писателе юзать TryCriticalSection в цикле с задержкой.
А вообще надо у МВо еще спросить, он перевел книженцию, может чето добавит.


 
Игорь Шевченко ©   (2005-06-03 10:43) [27]

Alexander Panov ©   (02.06.05 18:32) [2]

И нафига, спрашивается, из простого делать сложное ?


 
Alexander Panov ©   (2005-06-03 10:53) [28]

Игорь Шевченко ©   (03.06.05 10:43) [27]
И нафига, спрашивается, из простого делать сложное ?


Не понял мысль-)


 
Игорь Шевченко ©   (2005-06-03 10:58) [29]

Alexander Panov ©   (03.06.05 10:53) [28]

У автора изначальной классический алгоритм многопоточного анализа и заполнения неких данных (ну, бывает). Зачем при этом вводить какие-то дополнительные условия вроде "предполагаемых требований к потокам" и условия 3, мне совсем непонятно.


 
Verg ©   (2005-06-03 10:58) [30]

 S : TCriticalSection;
 ReadFlag ; boolean;

procedure WriteBuffer( const Data; Size : cardinal );
label l1;
begin
 l1:
   EnterCriticalSection( S );

   if( ReadFlag ) then
   begin
     LeaveCriticalSection( S )
     Goto l1;
   end;

 InternalWriteBuffer( Data, Size );

 LeaveCriticalSection( S );
end;

function ReadBuffer( var Data; BufferSize : cardinal ) : cardinal;
begin
 ReadFlag := true;
 EnterCriticalSection( S );
 Result := InternalReadBuffer( Data, BufferSize );
 ReadFlag := fasle;
 LeaveCriticalSection( S ) ;
end;


 
evvcom ©   (2005-06-03 11:00) [31]


> у вас несколько искаженное понимание первоначальной задачи

Возможно. На протяжении стольких постов понимание могло исказиться.

> Парсингом занимается не R поток, а W потоки. Закачивает страницы какой-то "блок закачки".

Тогда уж поток парсинга можно назвать RW, причем, как я уже писал, его одного будет достаточно, имхо. Ему ж надо прочитать флаг, что "закачено", а потом записать флаг "распарсено" :) А "блок закачки" - это несколько потоков W


 
leonidus ©   (2005-06-03 11:00) [32]

Я не пойму, а как будут между собой взаимодействовать две критические секции защищающие один ресурс? Для одной К.C. все ясно, на как работают две?


 
evvcom ©   (2005-06-03 11:07) [33]


> Ты же сам написал - в настоящей реализации ОС запросы на
> крит. секции идут fifo для потоков одинаковых приоритетов.

Но я также писал, что это незадокументировано, поэтому ставку на это делать нельзя. И кроме того, я также не писал, что ОС отберет тут же у W1 его квоту и передаст R. W1 (при одинаковых приоритетах) может успеть покинуть и S1. И в этом случае, даже в текущей реализации ОС, квант времени наверняка получит (хотя я не утверждаю, что это факт) получит W2 и свободно займет обе секции.


 
Игорь Шевченко ©   (2005-06-03 11:07) [34]

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


 
evvcom ©   (2005-06-03 11:08) [35]


> Для одной К.C. все ясно, на как работают две?

независимо друг от друга, вследствие чего легко получить deadlock


 
evvcom ©   (2005-06-03 11:10) [36]


> Игорь Шевченко ©   (03.06.05 11:07) [34]

Вот и я примерно о том же давно вопрошаю. Могу согласится, что чтение все же полезно по мере поступления обработанной информации, чтобы юзер не уснул в кресле. :)


 
Alexander Panov ©   (2005-06-03 11:21) [37]

У Verg ©   (03.06.05 10:58) [30] - нужная схема.


 
Alexander Panov ©   (2005-06-03 11:24) [38]

Недостаток, который нужно устранять в Verg ©   (03.06.05 10:58) [30] - при ReadFlag=True записывающие потоки не должны пытаться получтьб критическую секцию.


 
Игорь Шевченко ©   (2005-06-03 11:27) [39]

evvcom ©   (03.06.05 11:10) [36]


> Могу согласится, что чтение все же полезно по мере поступления
> обработанной информации,


И каким образом определяется, что информация пригодна для чтения ? Если каждый информация, представленная каждым пишушим потоком, не зависит от информации, представленной другими пишущими потоками, то это, опять же, классическая задача "производитель/потребитель", в которой многопточность используется для повышения скорости производителя (например, один поток производителя долго ожидает какого-то ввода-вывода для получения необходимых для начала разбора данных, а другие в это время занимаются разбором). В таком случае имеет смысл организовать очередь, куда все производящие потоки помещают результаты своей деятельности, а потоки потребителя опустошают эту очередь. Реализаций решения такого класса задач множество и они давно известны.
Если же результаты разбора имеют смысл только вместе (например, при многопоточной закачке одного файла с web-узла), когда части файла по отдельности смысла не имеют, то реализация напрашивается следующая - создается набор объектов "событие", изначально в занятом состоянии, каждый поток-производитель переводит свой объект "событие" в свободное состояние, а поток-потребитель вызывает WaitForMultipleObjects с параметром bWaitAll равным true для того, чтобы гарантировано дождаться завершения всех потоков-производителей.


 
Verg ©   (2005-06-03 11:30) [40]


> Alexander Panov ©   (03.06.05 11:24) [38]


при ReadFlag=True записывающие потоки не должны пытаться получтьб критическую секцию.

Почему? Они просто снова встанут на ожидании ее освобождения сразу после захвата секции читающим. А пока читающему секция не предоставлена - да, они будут поочереди "проворачиваться" через тот самый goto...


 
evvcom ©   (2005-06-03 11:31) [41]

Полностью согласен, Игорь.


 
Alexander Panov ©   (2005-06-03 11:32) [42]

Verg ©   (03.06.05 11:30) [40]
Они просто снова встанут на ожидании ее освобождения сразу после захвата секции читающим


Читающий еще должен попасть в критическую секцию. Почему ты думаешь, что в секцию зайдет читающий, а не пишущий поток?


 
Игорь Шевченко ©   (2005-06-03 11:33) [43]

Критическая секция в первом случае нужна, как для читателей, так и для писателей, во втором случае нужна только для писателей, чтобы они не путались друг с другом.


 
Alexander Panov ©   (2005-06-03 11:37) [44]

Игорь Шевченко ©   (03.06.05 10:58) [29]
Вообще это вполне реальная задача(та, которую я обрисовал).

Потребителю в этой задаче не нужно дожидаться, пока все производители закончат свою работу.

Например, потребитель непрерывно обрабатывает данные, которые выдают производители. Эта обработка занимает определенное время. После окончания обработки порции данных потребитель вновь обращается к буферу за новой порцией.

Не вижу в этой задаче ничего надуманного.


 
Verg ©   (2005-06-03 11:37) [45]


> Почему ты думаешь, что в секцию зайдет читающий, а не пишущий
> поток?


Читающий поток встает на ожидании S, предварительно взведя ReadFlag. Если к этому моменту времени ее уже ожидали N W-еров, то при получении S они тут же будут возвращаться к ожиданию S (вставать в конец очереди), пока эта очередь не дойдет до читающего.

Т.е. пишуший(е) поток(и) могут попасть в S раньше читающего, но они ее немедленно тут же и покинут, обнаружив присутстваие ридера в очереди ожидания S.


 
Alexander Panov ©   (2005-06-03 11:40) [46]

Verg ©   (03.06.05 11:37) [45]
Читающий поток встает на ожидании S, предварительно взведя ReadFlag. Если к этому моменту времени ее уже ожидали N W-еров, то при получении S они тут же будут возвращаться к ожиданию S (вставать в конец очереди), пока эта очередь не дойдет до читающего.


Я ничего не знаю про очередность. Сомневаюсь, что она каким-либо образом соблюдается.
R может долго не получить секцию, пока W крутятся вхолостую, нагружая процессор ненужной работой.


 
Игорь Шевченко ©   (2005-06-03 11:42) [47]

Alexander Panov ©   (03.06.05 11:37) [44]


> Потребителю в этой задаче не нужно дожидаться, пока все
> производители закончат свою работу.
>
> Например, потребитель непрерывно обрабатывает данные, которые
> выдают производители. Эта обработка занимает определенное
> время. После окончания обработки порции данных потребитель
> вновь обращается к буферу за новой порцией.


Очередь. Классика.


 
evvcom ©   (2005-06-03 11:45) [48]


> Например, потребитель непрерывно обрабатывает данные, которые
> выдают производители. Эта обработка занимает определенное
> время. После окончания обработки порции данных потребитель
> вновь обращается к буферу за новой порцией.

Задача реальная. Но зачем держать крит.секцию все время обработки данных? Ведь секция здесь нужна только для того, чтобы читать/писать массив флагов/адресов и т.п. А сами данные пусть обрабатываются после освобождения секции.


 
Verg ©   (2005-06-03 11:56) [49]

> Alexander Panov ©   (03.06.05 11:40) [46]
> Verg ©   (03.06.05 11:37) [45]
> Читающий поток встает на ожидании S, предварительно взведя
> ReadFlag. Если к этому моменту времени ее уже ожидали N
> W-еров, то при получении S они тут же будут возвращаться
> к ожиданию S (вставать в конец очереди), пока эта очередь
> не дойдет до читающего.
>
> Я ничего не знаю про очередность. Сомневаюсь, что она каким-либо
> образом соблюдается.
> R может долго не получить секцию, пока W крутятся вхолостую,
> нагружая процессор ненужной работой.


Это не проблема. writer-ов можно пустить в "отстойник" на время работы ридера.

 S : TCriticalSection;
 ReaderInQue : boolean;
 ReadFlag : TEvent; // Manual reset, initial state = TRUE

procedure WriteBuffer( const Data; Size : cardinal );
label l1;
begin
 l1:
   EnterCriticalSection( S );

   if( ReaderInQue ) then
   begin
     LeaveCriticalSection( S )
     // Вариант с двумя КС- то же нормальный, но в моем мы имеем возможность направить writer-ов выполнять некоторый полезный код, пока с буфером "хозяйничает" ридер
     // Вот сдесь прямо.
     WaitForSingleObject( ReadFlag, INFINITE );
     Goto l1;
   end;

 InternalWriteBuffer( Data, Size );

 LeaveCriticalSection( S );
end;

function ReadBuffer( var Data; BufferSize : cardinal ) : cardinal;
begin
 ResetEvent( ReadFlag );
 ReaderInQue := true;
 EnterCriticalSection( S );
 Result := InternalReadBuffer( Data, BufferSize );
 SetEvent( ReadFlag );
 ReaderInQue := fasle;

 LeaveCriticalSection( S ) ;
end;


 
Igorek ©   (2005-06-03 12:00) [50]

evvcom ©   (03.06.05 11:07) [33]
Ты противоречишь сам себе в этом посте. Ты путаешь квоты и приоритеты крит. секций.


 
Igorek ©   (2005-06-03 12:08) [51]

2 Verg ©
Написано же:

> 1. Не должно быть холостых циклов(while Flag<>Value do)


> Другие объекты синхронизации(Mutex, Event, и подобные)
> использовать необходимости нет.


 
Игорь Шевченко ©   (2005-06-03 12:10) [52]

Igorek ©   (03.06.05 12:00) [50]


> квоты и приоритеты крит. секций.


А эти звери из какого зоопарка ?


 
Alexander Panov ©   (2005-06-03 12:10) [53]

Igorek ©   (03.06.05 12:08) [51]

Как раз последняя схема полностью удовлетворяет постановке задачи.


 
Alexander Panov ©   (2005-06-03 12:13) [54]

В результате видно, что такая схема строится все-таки с использованием флагов(Не считая критических секций, хотя можно и без них обойтись).

Если кто приведет рабочую схему без использования объектов ядра для ожидания, буду благодарен.


 
evvcom ©   (2005-06-03 12:14) [55]


> Igorek ©   (03.06.05 12:00) [50]

А без голословных "обвинений" можно?


 
Игорь Шевченко ©   (2005-06-03 12:15) [56]

По поводу очереди: критической секцией защищается только сама очередь и ничего более. То есть, вход в критическую секцию производится только в момент постановки в очередь и чтения из нее. Вполне допустимо для ее реализации использовать слегка доработанный TThreadList.


 
Igorek ©   (2005-06-03 12:53) [57]

Игорь Шевченко ©   (03.06.05 12:10) [52]
Ни из какого, ибо таких зверей не существует.

evvcom ©   (03.06.05 12:14) [55]
Ты же сам написал, что кто первый запросил крит. секцию, тот первый ее и получит.
А потом пишешь, что порядок распределения квот проц. времени может это изменить.

Alexander Panov ©   (03.06.05 12:10) [53]
Как раз последняя схема полностью удовлетворяет постановке задачи.

Так вашей же постановке в [2] не удовлетворяет.


 
Alexander Panov ©   (2005-06-03 12:56) [58]

Igorek ©   (03.06.05 12:53) [57]
Так вашей же постановке в [2] не удовлетворяет.


В приведенной схеме нет холостых циклов;)


 
Alexander Panov ©   (2005-06-03 12:57) [59]

Сейчас сделаю обертку, и можно положить в ящик-)


 
Игорь Шевченко ©   (2005-06-03 13:11) [60]

Igorek ©   (03.06.05 12:53) [57]

Тогда наверное несуществующие термины не стоит употреблять, тем более, в споре, не так ли ?


> Ты же сам написал, что кто первый запросил крит. секцию,
> тот первый ее и получит.
> А потом пишешь, что порядок распределения квот проц. времени
> может это изменить.


Порядок изменения предоставления процессора может изменить того, кто будет первым, разве не так ?


 
Igorek ©   (2005-06-03 13:52) [61]

Игорь Шевченко ©   (03.06.05 13:11) [60]
Тогда наверное несуществующие термины не стоит употреблять, тем более, в споре, не так ли ?

Зачем тогда употребляешь?


 
Игорь Шевченко ©   (2005-06-03 13:55) [62]

Igorek ©   (03.06.05 13:52) [61]


> Зачем тогда употребляешь?


Ой. А разве процитированный пост [50] не твой ?


 
GrayFace ©   (2005-06-03 14:57) [63]

Тут вообще не нужны писатели.
Моя схема: в буфер пишет только гл.поток. Остальные только посылают ему сообщения со строками, которые надо записать в буфер.

Igorek ©   (02.06.05 22:12) [7]
Хорошее решение, но для полного счастья такая поправка:
Читатель, завершив свое грязное дело, освобождает S2, передает квант проц. времени читателю, и только потом освобожает S1.

КаПиБаРа ©   (03.06.05 9:11) [21]
6  W1 выходит из S1 и продолжает парсить

Противоречит схеме автора.

Verg ©   (03.06.05 10:58) [30]
Наихудший вариант.

leonidus ©   (03.06.05 11:00) [32]
Я не пойму, а как будут между собой взаимодействовать две критические секции защищающие один ресурс? Для одной К.C. все ясно, на как работают две?

Крит. секции вообще не обязаны защищать какие-то ресурсы. Работают они по-простому: кто успел, тот захватывает, остальные, вызвавшие FCS.Enter, ждут. Потом кто-то из них пробуждается после вызова FCS.Leave. Соответственно, перебирай все варианты, учитывая, что в любой момент квант проц. времени может исхерпаться, и активизируется другой поток.

Alexander Panov ©   (03.06.05 12:56) [58]
В приведенной схеме нет холостых циклов;)

Зато есть объект синхронизации, отличный от крит. секции.


 
GrayFace ©   (2005-06-03 14:59) [64]

Описка:

Igorek ©   (02.06.05 22:12) [7]
Хорошее решение, но для полного счастья такая поправка:
Писатель, завершив свое грязное дело, освобождает S2, передает квант проц. времени читателю, и только потом освобожает S1.


 
Игорь Шевченко ©   (2005-06-03 15:08) [65]

GrayFace ©   (03.06.05 14:57) [63]

Матчасть ?


 
Igorek ©   (2005-06-03 15:16) [66]

Игорь Шевченко ©   (03.06.05 13:11) [60]
Порядок изменения предоставления процессора может изменить того, кто будет первым, разве не так ?

В контексте цитаты не так.
Игорь Шевченко ©   (03.06.05 13:55) [62]
Ой. А разве процитированный пост [50] не твой ?

Мой, а что?


 
Игорь Шевченко ©   (2005-06-03 15:20) [67]

Igorek ©   (03.06.05 15:16) [66]


> Мой, а что?


А то, что термины взяты из твоего поста. Вот и все.


> В контексте цитаты не так.


Тогда расскажи, как ты понял контекст цитаты :)


 
Igorek ©   (2005-06-03 15:40) [68]

Игорь Шевченко ©   (03.06.05 15:20) [67]
А то, что термины взяты из твоего поста. Вот и все.

В моем посте нету несуществующих терминов.
Игорь Шевченко ©   (03.06.05 15:20) [67]
Тогда расскажи, как ты понял контекст цитаты :)

Так это ж моя цитата! Тебе ее пояснить, что бы ты лучше понял? :)


 
Игорь Шевченко ©   (2005-06-03 15:49) [69]

Igorek ©   (03.06.05 15:40) [68]


> В моем посте нету несуществующих терминов.


Тогда разъясни мне фразу "Ты путаешь квоты и приоритеты крит. секций.".

Что такое квота крит. секции и приоритет крит. секции.


 
evvcom ©   (2005-06-03 16:09) [70]


> Что такое квота крит. секции

Я понял его так: "Ты путаешь (квоты) и (приоритеты крит. секций)"
насчет квоты все понятно, а насчет приоритетов...


 
Igorek ©   (2005-06-03 16:28) [71]

Игорь Шевченко ©   (03.06.05 15:49) [69]
Тогда разъясни мне фразу "Ты путаешь квоты и приоритеты крит. секций.".
Что такое квота крит. секции и приоритет крит. секции.

Квоту критической секции я не упоминал. Не знаю такого термина, и сожалею, что ты меня неправильно понял.
Есть:
квота - это та часть процессорного времени, которой пользуется данный поток после переключения контекста процессора на него (на данный поток) и до переключения на другой поток
приоритет критической секции - это правило, по которому из числа ожидающих занятую крит. секцию потоков (одинакового приоритета) выбирается один, которому эта секция после освобождения предоставляется;

А фраза означает, что следующее утверждение неверно:
"после освобождения критической секции ее захватит тот поток (из числа ее ожидающих естественно), на который переключится контекст (т.е. который получит квоту в этот момент)".
Очевидно, что крит. секцию получит тот поток, который определятся приоритетом.


 
Игорь Шевченко ©   (2005-06-03 16:48) [72]

Igorek ©   (03.06.05 16:28) [71]

Квота в твоей цитате, и в evvcom - это вообще-то квант называется.


> приоритет критической секции - это правило, по которому
> из числа ожидающих занятую крит. секцию потоков (одинакового
> приоритета) выбирается один, которому эта секция после освобождения
> предоставляется;


Нет такого правила.


> следующее утверждение неверно:
> "после освобождения критической секции ее захватит тот поток
> (из числа ее ожидающих естественно), на который переключится
> контекст (т.е. который получит квоту в этот момент)".
> Очевидно, что крит. секцию получит тот поток, который определятся
> приоритетом.


Утверждение вообще-то верно. Так как основным условием является переключение контекста (которое и определяется приоритетом).
А если учесть, что ожидание критической секции происходит на объекте "событие", то диспетчеризация потоков, ожидающих освобождения критической секции, происходит согласно обычному списку.


 
Verg ©   (2005-06-03 17:01) [73]


> Igorek ©   (03.06.05 12:08) [51]
> 2 Verg ©
> Написано же:
>
> > 1. Не должно быть холостых циклов(while Flag<>Value do)


Один поток (W) пройдет через псевдо-цикл (goto) максимум один раз, прежде чем получит доступ к буферу. Это цикл не холостой, а выполняющий это действие:

> 3. В некий момент времени R получает команду прочитать буфер.
>  При этом все остальные потоки W должны немедленно прекратить
> свою работу по записи буфера
, кроме потока, который в данный
> момент заполняет буфер.


 
Alexander Panov ©   (2005-06-03 17:02) [74]

Вобщем между делом наклепал пример.

Может излишне перегружен код, но меня всегда подводит тяга к универсализму.

unit uRW;

interface

uses
 Classes, windows, SyncObjs;

type

 TWRProc=procedure(Sender: TObject);
 TTypeWR=(twrReader,twrWriter);

 TBuffer=class
 private
   FFlagRead: THandle;
   FCS: TCriticalSection;

   FProcOnWrite: TWRProc;
   FProcOnRead: TWRProc;

   function GetOnWrite: TWRProc;
   procedure SetOnWrite(const Value: TWRProc);

   function GetOnRead: TWRProc;
   procedure SetOnRead(const Value: TWRProc);

   function GetFlagRead: Boolean;
   procedure SetFlagRead(const Value: Boolean);

 public
   constructor Create;
   destructor Destroy; override;

   procedure Enter;
   procedure Leave;

   procedure WriteBuf(Sender: TObject;aProc: TWRProc);
   procedure ReadBuf(Sender: TObject;aProc: TWRProc);

   property OnWrite: TWRProc read GetOnWrite write SetOnWrite;
   property OnRead: TWRProc read GetOnRead write SetOnRead;

   property FlagRead: Boolean read GetFlagRead write SetFlagRead;

   procedure WaitReader;
  end;

 TWR = class(TThread)
 private
   FReadEvent: THandle;
   FType: TTypeWR;
   FBuf: TBuffer;
   FProc: TWRProc;
   procedure Display;
 protected
   procedure Execute; override;
 public
   constructor Create(aBuf: TBuffer;Proc: TWRProc; aType: TTypeWR);
   destructor Destroy; override;

   procedure CheckBuffer;
   property IOProc: TWRProc read FProc write FProc;
 end;

implementation

{ TBuffer }

constructor TBuffer.Create;
begin
 FFlagRead := CreateEvent(nil,True,False,nil);
 FCS := TCriticalSection.Create;
end;

destructor TBuffer.Destroy;
begin
 CloseHandle(FFlagRead);
 FCS.Free;
end;

procedure TBuffer.Enter;
begin
 FCS.Enter;
end;

function TBuffer.GetFlagRead: Boolean;
begin
 Result := ( WaitForSingleObject(FFlagRead,0)=WAIT_OBJECT_0 );
end;

function TBuffer.GetOnRead: TWRProc;
begin
 Result := FProcOnRead;
end;

function TBuffer.GetOnWrite: TWRProc;
begin
 Result := FProcOnWrite;
end;

procedure TBuffer.Leave;
begin
 FCS.Leave;
end;

procedure TBuffer.ReadBuf(Sender: TObject;aProc: TWRProc);
begin
 if Assigned(aProc) then
 begin
   FlagRead := False;
   Enter;
   try
     aProc(Sender);
   finally
     FlagRead := True;
     Leave;
   end;
 end;
end;

procedure TBuffer.SetFlagREad(const Value: Boolean);
begin
 case Value of
   True: SetEvent(FFLagRead);
   False: ResetEvent(FFLagRead);
 end;
end;

procedure TBuffer.SetOnRead(const Value: TWRProc);
begin
 FProcOnRead := Value;
end;

procedure TBuffer.SetOnWrite(const Value: TWRProc);
begin
 FProcOnWrite := Value;
end;

procedure TBuffer.WaitReader;
begin
 WaitForSingleObject(FFlagRead, INFINITE);
end;

procedure TBuffer.WriteBuf(Sender: TObject;aProc: TWRProc);
begin
 if Assigned(aProc) then
 begin
   WaitReader;
   Enter;
   try
     aProc(Sender);
   finally
     Leave;
   end;
 end;
end;

{ TWR }

procedure TWR.CheckBuffer;
begin
 SetEvent(FReadEvent);
end;

constructor TWR.Create(aBuf: TBuffer; Proc: TWRProc; aType: TTypeWR);
begin
 inherited Create(True);
 if FType=twrReader then FReadEvent := CreateEvent(nil,True,False,nil);
 FBuf := aBuf;
 FProc := Proc;
 FType := aType;
 Resume;
end;

destructor TWR.Destroy;
begin
 if FType=twrReader then CloseHandle(FReadEvent);
 inherited Destroy;
end;

procedure TWR.Display;
begin
 case FType of
   twrReader: if Assigned(FBuf.OnRead) then FBuf.OnRead(Self);
   twrWriter: if Assigned(FBuf.OnWrite) then FBuf.OnWrite(Self);
 end;
end;

procedure TWR.Execute;
begin
 Randomize;
 while not Terminated do
 begin
   if FType=twrReader
     then FBuf.ReadBuf(Self,FProc)
     else FBuf.WriteBuf(Self,FProc);
   Synchronize(Display);

   if FType=twrReader then
   begin
     if WaitFOrSingleObject(FReadEvent,10000)=WAIT_OBJECT_0
       then ResetEvent(FReadEvent);
   end
   else Sleep(Random(1000)+100);
 end;
end;

end.


Использование:


unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls,
 uRW;

type
 TForm1 = class(TForm)
   Button1: TButton;
   Memo1: TMemo;
   Button2: TButton;
   procedure Button1Click(Sender: TObject);
   procedure Button2Click(Sender: TObject);
   procedure FormCreate(Sender: TObject);
   procedure FormClose(Sender: TObject; var Action: TCloseAction);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 Form1: TForm1;
 Reader: TWR;
 Writer: TWR;
 List: TStringList;
 Buf: TBuffer;

implementation

{$R *.dfm}

procedure ProcRead(Sender: TObject);
var
 s: String;
begin
 s := IntToStr(TWR(Sender).ThreadID)+": Reader прочитал данные";
 Form1.Memo1.Lines.Add(s);
end;

procedure ProcWrite(Sender: TObject);
var
 s: String;
begin
 s := IntToStr(TWR(Sender).ThreadID)+": Writer записал данные";
 Form1.Memo1.Lines.Add(s);
end;

procedure Wr(Sender: TObject);
var
 s: String;
begin
 s := IntToStr(TWR(Sender).ThreadID);;
 List.Add(s);
 Sleep(100);
end;

procedure Re(Sender: TObject);
begin
 while List.Count>0 do
 begin
   List.Delete(0);
 end;
 Sleep(100);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 i: Integer;
begin
 for i := 0 to 9 do TWR.Create(Buf,Wr,twrWriter);
 Reader := TWR.Create(Buf,Re,twrReader);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
 Reader.CheckBuffer;
 Memo1.Lines.Add("Запрос чтения");
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 List := TStringList.Create;
 Buf := TBuffer.Create;
 Buf.OnRead := ProcRead;
 Buf.OnWrite := ProcWrite;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 List.Free;
end;

end.


 
evvcom ©   (2005-06-03 17:02) [75]


> Квота в твоей цитате, и в evvcom - это вообще-то квант называется

Точно. Литературу давно читал, а пользоваться терминами не приходилось, подзабыл. Сразу никто не поправил, вот и ругались неправильными словами.


 
Igorek ©   (2005-06-03 17:30) [76]

Игорь Шевченко ©   (03.06.05 16:48) [72]
Квота в твоей цитате, и в evvcom - это вообще-то квант называется.

Да квант.//мне этот термин не очень нравится, т.к. квант это обычно нечто неделимое, а процессорный квант имеет переменную длину (что подразумевает его делимость)

Короче. Вот цитирую Рихтера:
"EnterCriticalSection выполняет следующие действия.
- Если ресурс свободен, ..//бла-бла
- Если значения элементов структуры свидетельствуют, что ресурс уже захвачен вызывающим потоком, ..//бла-бла
- Если значения элементов структуры указывают на то, что ресурс занял другим потоком, EnterCriticalSection переводит вызывающий поток в режим ожидания. Это потрясающее свойство критических секций: поток, пребывая в ожидании, не тратит ни кванта процессорного времени Система запоминает, что данный поток хочет получить доступ к ресурсу, и - как только поток, занимавший этот ресурс, вызывает LeaveCriticalSection — вновь начинает выделять нашему потоку процессорное время...
...
В конце участка кода, использующего разделяемый ресурс, должен присутствовать вызов.
VOID LeaveCriticalSection(PCRITICAL_SECTION pcs);
Эта функция просматривает элементы структуры CRITICAL_SECTION и уменьша ет счетчик числа захватов ресурса вызывающим потоком на 1. Если его значение больше 0, LeaveCriticalSection ничего не делает и просто возвращает управление.
Если значение счетчика достигло 0, LeaveCnitcalSection сначала выясняет, есть ли в системе другие потоки, ждущие данный ресурс в вызове EnlerCriticalSection Если есть хотя бы один такой поток, функция настраивает значения элементов структуры, что бы они сигнализировали о занятости ресурса, и отдает его одному из ждущих потоков (поток выбирается «по справедливости»)"
Джеффри РИХТЕР
WINDOWS
Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows
ГЛАВА 8 Синхронизация потоков в пользовательском режиме

Вопросы?


 
Игорь Шевченко ©   (2005-06-03 17:44) [77]

Alexander Panov ©   (03.06.05 17:02) [74]

У меня вопрос - а что эта программа делает, кроме того, что память кушает ?
Я ее честно скомпилировал, запустил. Не смог остановить, пришлось снимать.


 
Игорь Шевченко ©   (2005-06-03 17:48) [78]

Igorek ©   (03.06.05 17:30) [76]


> Вот цитирую Рихтера:


И нафига, спрашивается ?


 
Alexander Panov ©   (2005-06-03 17:49) [79]

Игорь Шевченко ©   (03.06.05 17:44) [77]

-)
это пример, и не более того. Сделан как иллюстрация к задаче в [2].

Исходные классы позволяют организовать схему работы, как изначально св топике ставил задачу.

Память он не кушает зря. Просто в Memo1 не удаляются строки.


 
Игорь Шевченко ©   (2005-06-03 17:55) [80]

Alexander Panov ©   (03.06.05 17:49) [79]

Я несколько к другому. То есть, мне из запущенного примера даже непонятно, корректно ли она работает или некорректно. Уж если делать демку, то так, чтобы было видно, в одном мемо, например, записанные данные, в другом - прочитанные, чтобы можно было проверить, что то, что записано, оно и прочиталось. Чтобы можно было остановить процесс, наконец :)


 
Alexander Panov ©   (2005-06-03 18:01) [81]

Игорь Шевченко ©   (03.06.05 17:55) [80]
-)
Данных-то много, как же их отобразишь?


 
Alexander Panov ©   (2005-06-03 18:01) [82]

Хотя и такой пример можно нарисовать


 
Игорь Шевченко ©   (2005-06-03 18:06) [83]


> Данных-то много, как же их отобразишь?


В двух мемо с возможностью паузы для просмотра.


 
Igorek ©   (2005-06-03 18:16) [84]

Игорь Шевченко ©   (03.06.05 17:48) [78]
И нафига, спрашивается ?

Мда. Устал, ухожу.


 
Игорь Шевченко ©   (2005-06-03 21:44) [85]

Igorek ©   (03.06.05 18:16) [84]

То, что ты процитировал из Рихтера, никоим боком не относится к дискуссии о терминологии, и, более того, никоим боком не опровергает ничьи аргументы. Потому, собственно и вопрос.


 
Igorek ©   (2005-06-04 13:07) [86]

Игорь Шевченко ©   (03.06.05 21:44) [85]
Процитированое в [76] относится практически ко всему в данной ветке и сразу все ставит на свои места.
--
Ладно.
Желающим предлагается самостоятельно доказать почему:
1) схема в [7] есть решением задачи [2] с ограничениями и не требует вообще никаких дополнительных модификаций (разве только уточнения, что писатель освобождает S2, а потом S1)
2) алгоритмы от Verg © не решают задачу [2] ни с ограничениями ни без
--
2panov
Вот вам подробнее.

//Сначала главный поток:
InitializeCriticalSection(S1)
InitializeCriticalSection(S2)
//..
//Читатель:
EnterCriticalSection(S2);
//чтение
LeaveCriticalSection(S2);
//..
//Писатель:
EnterCriticalSection(S1);
EnterCriticalSection(S2);
//запись текущей порции данных
LeaveCriticalSection(S2);
LeaveCriticalSection(S1);
//..
//И в конце главный поток:
DeleteCriticalSection(S1);
DeleteCriticalSection(S2);


 
Alexander Panov ©   (2005-06-04 16:24) [87]

Igorek ©   (04.06.05 13:07) [86]
2) алгоритмы от Verg © не решают задачу [2] ни с ограничениями ни без


Мне сложно сформулировать, что это означает. Есть 3 варианта:

1. Демагогия(так как нет никаких аргуменов).
2. Ламеризм(см. п.1).
3. Тупая упертость(см. пп.1-2).

Ну и п.4 - Нетерпимость к чужому мнению, причем бездоказательно.
--------------------------------
Из. пп.1-3 не следует, что приведенная Verg схема является единственным решением.
--------------------------------

Прошу не обижаться, так как просто такое мнение создается, когда без аргументов пытаются охаять одно и точно так же без аргументов, кинуть свою идею.
--------------------------------

Что касается решения с 2-мя CS.

Решение красивое.
И на 99,9% я уверен, что он рабочее.
Но одно сомнение закрадывается вот в каком плане:

В момент окончания записи текущей порции данных

EnterCriticalSection(S1);
EnterCriticalSection(S2);
//запись текущей порции данных
LeaveCriticalSection(S2);
LeaveCriticalSection(S1);


Читающий поток находится в ожидании S2.
В этот момент писательпокидает секции S2, затем S1. Другой W пытается входит в S1, затем пытается войти в секцию S2.

Может ли другой W войти в секцию S2 раньше, чем R, так как в отношении выделения ресурсов система не поддерживает принцип очереди "Первый вошел, первый вышел"?

Если такой вариант возможен, то схема неработоспособна.
Это единственное сомнение.


 
Alexander Panov ©   (2005-06-04 16:26) [88]

Igorek ©   (04.06.05 13:07) [86]

И, кстати, сапсибо за решение. Весьма простая схема.


 
Verg ©   (2005-06-04 17:41) [89]


> Мне сложно сформулировать, что это означает.


Мне тоже. Кроме того, что я и не пытался опровергнуть очевидности решения с двумя КС, я просто предложил другой вариант (альтарнатива, если помнит кое-кто такое слово). Кому не нравится, тому не нравится.
Про аргументацию здесь, видимо, неприлично спрашивать вообще.

"не кажется ли вам. уважаемые судари, что сабж перешел в статус "мерянье концами" ?)"

(С) Digitman
http://delphimaster.net/view/1-1117521494/


 
Igorek ©   (2005-06-04 17:54) [90]

Alexander Panov ©   (04.06.05 16:24) [87]
Есть 3 варианта:
Ну и п.4

Могу посоветовать расширять кругозор.
Alexander Panov ©   (04.06.05 16:24) [87]
охаять

Понимайте как хотите. Если человек неправ, я говорю об этом прямо "вы неправы". Я же не говорю "вы или демагог или ламер или упертый тупица или нетерпимы к чужому мнению, причем бездоказательно". Аргументирую по мере сил и времени. Ключевым аргументом является пост [76] (уж не знаю как выделить), после которого все ясно. Я сам сомневался в некоторых вещах, пока не глянул Рихтера.

Alexander Panov ©   (04.06.05 16:24) [87]
Читающий поток находится в ожидании S2.
В этот момент писательпокидает секции S2, затем S1. Другой W пытается входит в S1, затем пытается войти в секцию S2.
Может ли другой W войти в секцию S2 раньше, чем R, так как в отношении выделения ресурсов система не поддерживает принцип очереди "Первый вошел, первый вышел"?
Если такой вариант возможен, то схема неработоспособна.
Это единственное сомнение.

Сомнение совершенно беспочвенное. Во-первых потоки, которые ждут. крит секцию не жрут кванты процессора, и потому не учавствуют в дипетчеризации (это к тому, что квант времени попадет другому писателю, ну и перехват крит. секции - неверно мягко говоря). Во-вторых как только писатель освобождает S2, эту секцию сразу же захватывает читатель, что очевидно из второго абзаца цитаты. Читатель сразу включается в диспетчеризацию. Хотя вопрос "получит ли он сразу квант времени?" для меня лично остается открытым. Ну и принцип fifo тут не в тему, согласитесь (ожидает то только один поток).
--
Все, бегу на день рождения.. :)


 
Verg ©   (2005-06-04 18:12) [91]


> Igorek ©   (04.06.05 17:54) [90]


> Понимайте как хотите. Если человек неправ, я говорю об этом
> прямо "вы неправы". Я же не говорю "вы или демагог или ламер
> или упертый тупица или нетерпимы к чужому мнению, причем
> бездоказательно".


Нет уж, дорогой, вы не говорите "вы не правы", вы высокомерно предлагаете другим это сказать ("Желающим предлагается самостоятельно доказать почему").

Так что, "ля-ля" это все. Пустой звук.


 
Alexander Panov ©   (2005-06-04 18:16) [92]

Igorek ©   (04.06.05 17:54) [90]
Хотя вопрос "получит ли он сразу квант времени?" для меня лично остается открытым.


Вопрос не в том, получит ли читающий поток квант времени сразу, а в том, не успеет ли до этого другой поток войти в критическую секцию.

В варианте от Verg такой вариант исключен.


 
Igorek ©   (2005-06-05 12:13) [93]

Verg ©   (04.06.05 17:41) [89]
"мерянье концами" ?)"

Бабы мерялись на даче,
у кого звезда лохмаче.
Оказалось, что лохмаче,
у самой хозяйки дачи!

Ладно, не будем сорится. :)
Если уж бабы любят мерятся, то мужики и подавно. :))

--
Теперь по сути, по [30].
Возьмем пример:
Есть 100 потоков (99 W, и 1 R).
Все ломанулись к буферу. Читатель ломанулся последним. Возникла ситуация - один писатель захватил крит. секцию, но не успел проверить флажок. Остальные 98 и читатель зависли в захвате. Писатель проверяет флажок, освобождает крит. секцию. Я сомневаюсь, что крит. секцию сразу получит читатель. Если действует fifo, то еще 98 писателей войдут и выйдут из крит. секции. Где я неправ?


 
Alexander Panov ©   (2005-06-05 14:05) [94]

Igorek ©   (05.06.05 12:13) [93]
Где я неправ?


вот здесь:
Если действует fifo, то еще 98 писателей войдут и выйдут из крит. секции.

Не действует fifo.


 
Alexander Panov ©   (2005-06-05 14:06) [95]

А пример оказалось весьма сложно сделать для проверки схемы с 2-мя критическими секциями.
МНе пока не удалось оттестироват момент, приведенный в [87] и [92].


 
Игорь Шевченко ©   (2005-06-06 11:11) [96]

Да сделайте вы обычную очередь и не мучайтесь


 
GrayFace ©   (2005-06-06 11:19) [97]

Игорь Шевченко ©   (03.06.05 15:08) [65]
Матчасть ?

Отчасти согласен. Передать квант проц. времени конкретному потоку нельзя - спутал. Но к какой части поста замечание?

Alexander Panov ©   (04.06.05 18:16) [92]
Вопрос не в том, получит ли читающий поток квант времени сразу, а в том, не успеет ли до этого другой поток войти в критическую секцию.

Если верить Рихтеру, распределение CS происходит при вызое LeaveCS, а не при раздаче кванта проц. времени. Крит. секция тут же отдается другому потоку и никто не может успеть ее захватить, т.к. все W ждут другой CS.


 
evvcom ©   (2005-06-06 14:06) [98]


> Если есть хотя бы один такой поток, функция настраивает
> значения элементов структуры, что бы они сигнализировали
> о занятости ресурса, и отдает его одному из ждущих потоков

Здесь нет слова "немедленно", по сему всё же только тестирование может доказать или опровергнуть достаточности только крит.секций для решения поставленной задачи. Хотя может кто-нибудь точно знает, что делает ntdll.RtlpUnWaitCriticalSection?


 
evvcom ©   (2005-06-06 21:58) [99]

Написал тест:

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls;

type
 TForm1 = class(TForm)
   Button1: TButton;
   Memo1: TMemo;
   procedure Button1Click(Sender: TObject);
 private
   procedure WMPrintMessage(var Message: TMessage); message WM_USER;
 public
   { Public declarations }
 end;

 TMyThread = class(TThread)
 private
   FName: string;
   FWriter: Boolean;
 protected
   procedure Execute; override;
 public
   constructor Create(AName: string; AWriter: Boolean);
 end;

var
 Form1: TForm1;
 List1: TStringList;
 CSR: TRTLCriticalSection;
 CSW: TRTLCriticalSection;
 CSL: TRTLCriticalSection;

implementation

{$R *.dfm}

procedure PrintMessage(AThread: TMyThread; AMessage: string);
var
 l_iIndex: Integer;
begin
 EnterCriticalSection(CSL);
 l_iIndex := List1.Add(Format("Поток %s: %s", [AThread.FName, AMessage]));
 PostMessage(Form1.Handle, WM_USER, l_iIndex, 0);
 LeaveCriticalSection(CSL);
end;

{ TMyThread }

constructor TMyThread.Create(AName: string; AWriter: Boolean);
begin
 FName := AName;
 FWriter := AWriter;
 FreeOnTerminate := True;
 inherited Create(False);
 Sleep(500);
end;

procedure TMyThread.Execute;
begin
 if FWriter then begin
   PrintMessage(Self, "Вход в крит. секцию CSW");
   EnterCriticalSection(CSW);
   PrintMessage(Self, "В крит. секцию CSW вошел");
 end;

 PrintMessage(Self, "Вход в крит. секцию CSR");
 EnterCriticalSection(CSR);
 PrintMessage(Self, "В крит. секцию CSR вошел. Вычисление");
 Sleep(5000);
 PrintMessage(Self, "Выход из крит. секции CSR"#13#10);
 LeaveCriticalSection(CSR);
 PrintMessage(Self, "Из крит. секции CSR вышел");

 if FWriter then begin
   PrintMessage(Self, "Выход из крит. секции CSW"#13#10);
   LeaveCriticalSection(CSW);
   PrintMessage(Self, "Из крит. секции CSW вышел");
 end;
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
 Memo1.Clear;
 TMyThread.Create("W1", True);
 TMyThread.Create("W2", True);
 TMyThread.Create("R", False);
end;

procedure TForm1.WMPrintMessage(var Message: TMessage);
begin
 EnterCriticalSection(CSL);
 Memo1.Lines.Add(List1.Strings[Message.WParam]);
 LeaveCriticalSection(CSL);
end;

initialization
 InitializeCriticalSection(CSR);
 InitializeCriticalSection(CSW);
 InitializeCriticalSection(CSL);
 List1 := TStringList.Create;

finalization
 DeleteCriticalSection(CSR);
 DeleteCriticalSection(CSW);
 DeleteCriticalSection(CSL);
 List1.Free;

end.

Результаты теста:

Поток W1: Вход в крит. секцию CSW
Поток W1: В крит. секцию CSW вошел
Поток W1: Вход в крит. секцию CSR
Поток W1: В крит. секцию CSR вошел. Вычисление
Поток W2: Вход в крит. секцию CSW
Поток R: Вход в крит. секцию CSR
Поток W1: Выход из крит. секции CSR

Поток R: В крит. секцию CSR вошел. Вычисление
Поток W1: Из крит. секции CSR вышел
Поток W1: Выход из крит. секции CSW

Поток W2: В крит. секцию CSW вошел
Поток W2: Вход в крит. секцию CSR
Поток W1: Из крит. секции CSW вышел
Поток R: Выход из крит. секции CSR

Поток W2: В крит. секцию CSR вошел. Вычисление
Поток R: Из крит. секции CSR вышел
Поток W2: Выход из крит. секции CSR

Поток W2: Из крит. секции CSR вышел
Поток W2: Выход из крит. секции CSW

Поток W2: Из крит. секции CSW вышел

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


 
Verg ©   (2005-06-07 06:03) [100]


> Igorek ©   (05.06.05 12:13) [93]


> Если действует fifo, то еще
> 98 писателей войдут и выйдут из крит. секции. Где я неправ?


Ну и ...? Так они и выйдут, они выполнят пункт 3 задания - "При этом все остальные потоки W должны немедленно прекратить свою работу по записи буфера, кроме потока, который в данный момент заполняет буфер."
Другими словами читатель потратит времени на чтение из буфера не более того, что тратит один писатель на запись плюс время собственно чтения, пусть их хоть тыща желающих записать.

program OneReader;

{$APPTYPE CONSOLE}

uses
 Windows,
 SysUtils,
 Classes,
 SyncObjs;

type
 TWriteThread = class( TThread )
 public
   constructor Create();
   procedure Execute; override;
 end;

var
S : TCriticalSection;
ReaderInQue : integer;

 FCount  : integer;
 FAvgTime : int64;
 FMaxTime : cardinal;

procedure InternalWriteBuffer( const Data; Size : cardinal );
begin
 Sleep( 500 );
end;

function InternalReadBuffer( var Data; BufferSize : cardinal ) : cardinal;
begin
 Result := 0;
end;

procedure WriteBuffer( const Data; Size : cardinal );
label l1;
begin
l1:
  S.Enter;

  if ReaderInQue <> 0 then
  begin
    S.Leave;
    Goto l1;
  end;

  InternalWriteBuffer( Data, Size );

  S.Leave;
end;

function ReadBuffer( var Data; BufferSize : cardinal ) : cardinal;
begin
InterLockedIncrement( ReaderInQue );
S.Enter;

Result := InternalReadBuffer( Data, BufferSize );

InterLockedDecrement( ReaderInQue );
S.Leave;
end;

constructor TWriteThread.Create;
begin
 FreeOnTerminate := true;
 inherited Create( false );
end;

procedure   TWriteThread.Execute;
var D : integer;
begin
 while not Terminated do
 begin
   Sleep( Random(5) );
   WriteBuffer( D, sizeof(D));
 end;
end;

var i : integer;
   T : cardinal;
   D : integer;
begin
 Randomize;

 ReaderInQue :=0;
 FCount      :=0;
 FAvgTime    :=0;
 FMaxTime    :=0;

 S        := TCriticalSection.Create;

 for i:=1 to 300 do
 begin
   TWriteThread.Create;
 end;

 while true do
 begin
   Sleep(Random( 30 ) );
   T := GetTickCount;
   ReadBuffer( D, sizeof(D));
   T := GetTickCount - T;
   if( T > FMaxTime ) then FMaxTime := T;
   inc( FAvgTime, T );
   inc( Fcount) ;
   WriteLn( T, #9, FAvgTime / FCount : 3:2, #9, FMaxTime);
 end;

end.


 
evvcom ©   (2005-06-07 09:02) [101]

В дополнение к evvcom ©   (06.06.05 21:58) [99]:
Попытался потом попозже еще отладчиком прошагать по LeaveCriticalSection, RtlpUnWaitCriticalSection и др., чтобы хоть примерно увидеть, где происходит смена контекста потока. Результат - смены контекста не произошло, результаты изменились, и я увидел

Поток W1: Выход из крит. секции CSR
Поток W1: Из крит. секции CSR вышел
Поток W1: Выход из крит. секции CSW

и только потом где-то

Поток R: В крит. секцию CSR вошел. Вычисление

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


 
Alexander Panov ©   (2005-06-07 10:05) [102]

evvcom ©   (06.06.05 21:58) [99]
evvcom ©   (07.06.05 9:02) [101]

К сожалению, не только присутствие отладчика изменит порядок работы потоков, но и вывод отладочной информации.
В этом и трудность - написать тест, который не исказит результаты.


 
Igorek ©   (2005-06-07 10:22) [103]

evvcom ©   (07.06.05 9:02) [101]
где происходит смена контекста потока. Результат - смены контекста не произошло

Что такое "контекст потока"? Как его можно увидеть в отладчике?

evvcom ©   (07.06.05 9:02) [101]
что свидетельствует о том, что все же алгоритм не надежен

Неа. Это свидетельствует только о том, что читатель не получил квант времени сразу после захвата CSR и не успел послать сообщение. О чем я и писал:
Igorek ©   (04.06.05 17:54) [90]
Читатель сразу включается в диспетчеризацию. Хотя вопрос "получит ли он сразу квант времени?" для меня лично остается открытым.

Если ты правильно протестировал, то ответ - "не обязательно".

Verg ©   (07.06.05 6:03) [100]
Так они и выйдут, они выполнят пункт 3 задания - "При этом все остальные потоки W должны немедленно прекратить свою работу по записи буфера, кроме потока, который в данный момент заполняет буфер."

Вы слишко вольно трактуете термин "Немедленно". А я наверно слишком свободно термин "холостой" цикл. Собственно эти входы и выходы мне кажутся именно холостыми.
Verg ©   (07.06.05 6:03) [100]
Другими словами читатель потратит времени на чтение из буфера не более того, что тратит один писатель на запись плюс время собственно чтения, пусть их хоть тыща желающих записать.

А мильен? А мильярд? Время, которое читатель потратит времени на чтение из буфера вообще зависит от колл. писателей в очереди?

Кроме того в свете поста [101] возникает еще одно сомнение. Если писатель покидает крит. секцию, это еще не значит, что он потеряет контекст процессора. Значит он снова может успеть встать в очередь на крит. секцию. А учитывая, что fifo не гарантировано, даже еще раз ее захватить. Как-то это все меньше похоже на "немедленно".


 
Игорь Шевченко ©   (2005-06-07 10:25) [104]


> Что такое "контекст потока"? Как его можно увидеть в отладчике?


С правой стороны :)


 
evvcom ©   (2005-06-07 11:10) [105]


> Как его можно увидеть в отладчике?

Ctrl+Alt+T


 
Igorek ©   (2005-06-07 11:54) [106]

Игорь Шевченко ©   (07.06.05 10:25) [104]
С правой стороны :)

Не умничай. У меня там системный блок. А дальше мебель.. Что я делаю неправильно?!! :(


 
Игорь Шевченко ©   (2005-06-07 12:01) [107]

Igorek ©   (07.06.05 11:54) [106]


> У меня там системный блок. А дальше мебель


Переставь на другую сторону и увидишь контекст потока


 
evvcom ©   (2005-06-07 13:06) [108]

Еще идея. Каждый W, заметив запрос R, выполняет Suspend. R отработав, будит все W. Это предотвратит замечания [103].
Только мне интересен такой вариант, а если R, выставляя свой флаг, вызовет для всех W Suspend (ну кроме того, который уже возможно занял секцию CSR), не давая шансов им войти в секцию, не приведет ли это к глюкам?


 
GrayFace ©   (2005-06-09 10:16) [109]

Igorek ©   (07.06.05 11:54) [106]
Не умничай. У меня там системный блок. А дальше мебель.. Что я делаю неправильно?!! :(

:)))

Когда поток исчерпывает квант проц. времени регистры процессора и т.п. сохраняются в его контекст и загружаются из контекста потока, получившего квант.

evvcom ©   (07.06.05 13:06) [108]
Только мне интересен такой вариант, а если R, выставляя свой флаг, вызовет для всех W Suspend (ну кроме того, который уже возможно занял секцию CSR), не давая шансов им войти в секцию, не приведет ли это к глюкам?

Приведет к пустой трате времени, ведь основная часть W занята своей работой и на CS не претендуют.

PS Но лучший вариант - посылка сообщения со строкой.



Страницы: 1 2 3 вся ветка

Форум: "Потрепаться";
Текущий архив: 2005.07.11;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.86 MB
Время: 0.048 c
1-1118583202
Earth
2005-06-12 17:33
2005.07.11
Time


1-1118770859
Starcom
2005-06-14 21:40
2005.07.11
Build with Runtime packages


3-1117193378
highlander
2005-05-27 15:29
2005.07.11
Проблема использования сложных запросов с агрегатами


1-1119341906
salexn
2005-06-21 12:18
2005.07.11
Почему на 98 виндах возникает ошибка Cannot load cursor resource


14-1118732272
reticon
2005-06-14 10:57
2005.07.11
Не резольвятся адреса при пинге....





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский