скалогрызЯ ошибся, сделал тот же код на Дельфе, поведение такое же неадекватное.
"где такое написано?"
Я искал описания работы критической секции в подробностях и нашёл такое:
Когда поток выполняет вход в критическую секцию, он проверяет хозяина критической секции, если хозяин не он сам, то создаётся объект синхронизации, и поток начинает ожидать сигнальное состояние только, что созданного объекта. Когда поток выходит из критической секции, то выполняется проверка наличия созданного (кем-то другим) объекта синхронизации, если такой объект есть, то поток выходящий из КС, перестаёт быть хозяином и выставляет сигнальное состояние у объекта синхронизации.
Это можно назвать очередью из 1 одного элемента.
"гарантирует тот факт, что EnterCriticalSection 2 произойдёт только после того как отработал соседний поток ожидающий cs?" - Если честно, не очень понял этот вопрос.
Вы писали:
"
* Твой поток-счётчик, запускается и забирает критическую секцию (ну и спит с ней).
* В основном потоке, приходит сообщение: "Нажата кнопка".
* Основной поток идёт к крит-секции и ждёт пока она освободится (т.е. пока спит поток-счётчик).
* После захвата крит-секции он засыпает на 5 секунд (что вызывает эффект "залипания" т.к. другие сообщения не обрабатываются).
* (к этому времени, поток-счётчик уже ждёт своей очереди на сон, ожидая крит-секцию)
* Поспав пять секунд, основной поток возвращается в норму.
"
я с этим согласен, именно такого поведения я и ожидал, проблема в " Основной поток идёт к крит-секции и ждёт пока она освободится (т.е. пока спит поток-счётчик). ", основной поток никак не может дождаться критической секции не смотря на то, что поток-счётчик уходит в Sleep, что означает, что у основного потока куча времени на то, чтобы захватить КС.
" Исключать нужно короткие (по времени исполнения) куски кода." - что это значит? Как я уже писал, если внутри потока счётчика убрать Sleep (т.е. поток-счётчик только и будет входить в КС, увеличивать счётчик, выводить его, выходить из КС) , то приведённый в самом начале код, работает как ожидается.
"Пока работает Sleep - другие отдыхаю.." - поработает Sleep, другие работают. Sleep для каждого потока свой.
" у тебя один поток сильно зависит от другого.." - Тут как раз наоборот, поток-счётчик никак не хочет быть зависимым от основного потока, потому, что при попытке входа основным потоком в КС, поток счётчик работает так, как будто никто другой и не пытается войти в КС.
Добавлено спустя 10 минут 34 секунды:Вот
http://rsdn.org/article/baseserv/critsec.xml где написано. Там сказано, что поток который выходит из КС, сам проверяет наличие попытки другого потока войти в КС, если такая попытка была (пока он занимался своими делами, к примеру тупо спал), то поток хозяин КС выставляет объект синхронизации в сигнальное состояние, и поток который ожидал этот сигнальный объект пробуждается.
Добавлено спустя 5 минут 44 секунды:- Код: Выделить всё
repeat
CritSec.Acquire;
Inc(Counter);
WriteLn(Format('Counter=%d',[Counter]));
Sleep(100);
CritSec.Release;
[b]SwitchToThread;[/b]
until Self.Terminated;
Использование SwitchToThread; тоже не помогает (попробовал в Винде), как обычно, основной поток висит на ожидании КС, поток-счётчик, считает, выводит и не парится.
Добавлено спустя 24 минуты 13 секунд:Вот другой вариант на Дельфи.
- Код: Выделить всё
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, SyncObjs, ExtCtrls;
type
TSimpleThread = class(TThread)
protected
procedure Execute; override;
public
Delta:Integer;
end;
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Memo1: TMemo;
Button3: TButton;
Timer1: TTimer;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Timer1Timer(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
FSTh1:TSimpleThread;
FSTh2:TSimpleThread;
end;
var
Form1: TForm1;
CritSec:TCriticalSection;
Counter:Integer;
implementation
{$R *.dfm}
{ TSimpleThread }
procedure TSimpleThread.Execute;
begin
repeat
CritSec.Acquire;
Counter:=Counter+Self.Delta;
Sleep(100);
CritSec.Release;
until Self.Terminated;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
FSTh1:=TSimpleThread.Create(True);
FSTh1.FreeOnTerminate:=True;
FSTh1.Delta:=1;
FSTh2:=TSimpleThread.Create(True);
FSTh2.FreeOnTerminate:=True;
FSTh2.Delta:=-1;
FSTh1.Resume;
FSTh2.Resume;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
FSTh1.Terminate;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
CritSec.Acquire;
Memo1.Lines.Append(Format('Counter=%d',[Counter]));
Sleep(1000);
CritSec.Release;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
CritSec:=TCriticalSection.Create;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
CritSec.Free;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Memo1.Lines.Append(Format('Counter=%d',[Counter]));
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
FSTh2.Terminate;
end;
end.
Основной поток только выводит (по таймеру) значение счётчика в Memo. Один поток уменьшает счётчик, другой увеличивает. Казалось бы, если потоки используют одну критическую секцию, то значение счётчика не должно меняться, вместо этого счётчик постоянно меняется, значит один поток спокойно себе работает заходя в КС, меняя счётчик, и выходя из КС, а другой поток не может дождаться входа в КС, хотя другой поток постоянно захватывает и освобождает КС.