Sasha писал(а):Использование SwitchToThread; тоже не помогает (попробовал в Винде), как обычно, основной поток висит на ожидании КС, поток-счётчик, считает, выводит и не парится.
Если ты не можешь менять основной код (на чём я лично настаиваю), ТО я бы тебе посоветовал использовать уже не базовую критическую секцию, а критическую секцию с соблюдением очерёдности!
но, это уже объект, своими руками написанный. Могу помочь с его написанием.
Но ты кстати можешь попробовать базовую сделать, примерно вот так:
- Код: Выделить всё
type
TDoubleCS = class(TObject)
private
fmain : TRTLCriticalSection;
fgate : TRTLCriticalSection;
public
procedure Enter;
procedure Leave;
constructor Create;
destructor Destroy; override;
end;
{ TDoubleCS }
procedure TDoubleCS.Enter;
begin
EnterCriticalsection(fgate);
EnterCriticalsection(fmain);
LeaveCriticalsection(fgate);
end;
procedure TDoubleCS.Leave;
begin
LeaveCriticalsection(fmain);
end;
constructor TDoubleCS.Create;
begin
InitCriticalSection(fmain);
InitCriticalSection(fgate);
end;
destructor TDoubleCS.Destroy;
begin
DoneCriticalSection(fmain);
DoneCriticalSection(fgate);
inherited Destroy;
end;
var
...
d: TDoubleCS;
procedure TMyThread.Execute;
begin
repeat
//EnterCriticalsection(FCritSec);
d.Enter;
//SemaphoreWait(MySem);
Inc(Counter);
WriteLn(Format('Counter=%d',[Counter]));
Sleep(100);
//SemaphorePost(MySem);
//LeaveCriticalsection(FCritSec);
d.Leave;
//ThreadSwitch;
until CheckTerminated;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
InitCriticalSection(FCritSec);
d:=TDoubleCS.Create;
...
end;
Будет работать 100% - для этого примера!
Но это больше костыль, чем решение.
Sasha писал(а):Я искал описания работы критической секции в подробностях и нашёл такое:
ссылку бы. Но я бы не доверял сторонним источникам, единственный источник правды - официальная документация.
И её (официальную документацию) следует читать не только с точки зрения "того что написано", но и с точки зрения "того что не написано".
Т.е. если в официально документации не сказано, что очерёдность захвата критический секции соблюдается, то значит нужно думать, что она не соблюдается.
Sasha писал(а):Когда поток выполняет вход в критическую секцию, он проверяет хозяина критической секции, если хозяин не он сам, то создаётся объект синхронизации, и поток начинает ожидать сигнальное состояние только, что созданного объекта. Когда поток выходит из критической секции, то выполняется проверка наличия созданного (кем-то другим) объекта синхронизации, если такой объект есть, то поток выходящий из КС, перестаёт быть хозяином и выставляет сигнальное состояние у объекта синхронизации.
Это можно назвать очередью из 1 одного элемента.
тут немножко глупость написана: "если хозяин не он сам, то создаётся объект синхронизации" - объект синхронизации создан изначально (InitCriticalSection).
Поток просто ожидает сигнальное состояние, с частным случаем того, что если КС не занята, то она уже в сигнальном состоянии.
Но это не суть.
А суть в другом. Суть в том что состояние "ожидания" критической секции и состояние "захвата" критической секции, выполняются одной командой: EnterCriticalSection.
НО, это две разные и последовательные операции. (сначала "ждём", потом "захватываем")
Проблема в том, что КС может освободиться в любой момент времени.
И если в этот момент времени, поток не был запущен ОСью, то поток не сможет сделать "захват". и КС "захватит" кто-то другой.
Это очевидно при работе с несколькими потоками. 3 потока ожидают 1 КС. Когда КС была отпущена предыдущим хозяином, только тот из потоков кто был запущен первым системой, получит эту самую КС. (и не факт, что это будет тот поток, который начал её первым "ждать")
То же самое может происходить когда всего 2 потока работают с 1 КС.
Поток-1 "забрал" КС
Поток-2 "ожидает" КС
Поток-1 "отпустил" КС
Поток-1 снова "забрал" КС, (т.к. поток-2 не был запущен)
Sasha писал(а):Вот http://rsdn.org/article/baseserv/critsec.xml где написано...
я боюсь тебя вводят в заблуждение.
вот официальная документация на LeaveCriticalSection. Про то, что ожидающий поток будет
пробуждаться ничего не сказано.
Работа с объектами синхронизации не является работой планировщика задач (именно планировщик задач решает, когда и какому потоку на какой процессоре работать).