Зависание потоков при использовании критических секции

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Re: Зависание потоков при использовании критических секции

Сообщение скалогрыз » 22.10.2017 16:30:55

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. Про то, что ожидающий поток будет
пробуждаться ничего не сказано.
Работа с объектами синхронизации не является работой планировщика задач (именно планировщик задач решает, когда и какому потоку на какой процессоре работать).
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Зависание потоков при использовании критических секции

Сообщение Sasha » 22.10.2017 17:18:48

"Могу помочь с его написанием" был бы признателен за помощь.
На данный момент работает (как ожидается) такой вариант:
Код: Выделить всё
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;


type

  TMyThread=class(TThread)
  private
  protected
    procedure Execute; override;
  public
    Counter:Integer;
  end;


  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    FMyTh:TMyThread;
  end;


var
  Form1: TForm1;
  FCritSec:TRTLCriticalSection;

implementation

{$R *.lfm}

{ TMyThread }

procedure TMyThread.Execute;
var
  i:Integer;
begin
  repeat
    i:=0; repeat Inc(i); until i=750;
    EnterCriticalsection(FCritSec);
      Inc(Counter);
      WriteLn(Format('Counter=%d',[Counter]));
      Sleep(100);
    LeaveCriticalsection(FCritSec);
  until CheckTerminated;
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
  FMyTh:=TMyThread.Create(True);
  FMyTh.FreeOnTerminate:=True;
  FMyTh.Start;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  EnterCriticalsection(FCritSec);

  Memo1.Lines.Append(Format('Counter=%d',[FMyTh.Counter]));
  Sleep(5000);

  LeaveCriticalsection(FCritSec);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  if Assigned(FMyTh) then
   FMyTh.Terminate;
end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  Button3Click(nil);
  DoneCriticalsection(FCritSec);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  InitCriticalSection(FCritSec);
end;


end.


ключевая строка i:=0; repeat Inc(i); until i=750; задержка перед входом КС, занимает примерно 1.5 мкс (на моём компе), это вполне терпимо.
Что интересно этот вариант подходит для Лазаруса, а в Дельфи нужно ставить задержку до 5000, чтобы стабильно работало.
Не до конца понял Ваш вариант с 2-я КС, но покумекаю может пойму.

Добавлено спустя 19 минут 49 секунд:
С предыдущим решением не всё так гладко, сделал на Лазарусе вариант, который делал на Дельфи (главный поток только выводит значение счётчика, а 2 других его меняют каждый в свою сторону), в итоге паузу перед входом в КС нужно увеличивать. Короче и так понятно, что решение фиговое, просто теперь в этом есть убеждённость.
Sasha
новенький
 
Сообщения: 41
Зарегистрирован: 07.12.2015 01:27:43

Re: Зависание потоков при использовании критических секции

Сообщение Mirage » 22.10.2017 17:51:07

Прежде чем заниматься написанием многопоточного кода надо ознакомиться с теорией.
Во-первых поведение вполне адекватное. Поток спит, не отпуская крит. секцию, поэтому пока он спит никто ее захватить не может. После пробуждения он сразу же ее захватывает. Так что даже если основной поток и получил сигнал пробуждаться, это ничего ему не даст - снова "занято" кричат.
Во-вторых спать в крит. секции не надо. И делать всякое долгое, типа операций с сокетами, тоже не надо. Надо работать с общими данными - а именно, захватил крит. секцию, быстро-быстро записал что нужно в общие данные, освободил секцию.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Зависание потоков при использовании критических секции

Сообщение Sasha » 22.10.2017 18:14:43

"Надо работать с общими данными" - А елси сокет и есть общие данные?

Добавлено спустя 4 минуты 53 секунды:
Теория описана вот здесь
http://rsdn.org/article/baseserv/critsec.xml
и по ней не должно быть ситуации, при которой один поток постоянно работает, а другой постоянно ждёт.
В переменной для КС в Лазарусе, есть все те поля, которые описаны в http://rsdn.org/article/baseserv/critsec.xml, поэтому я полагал, что и реализация такая же, но видимо, это не так.

Добавлено спустя 8 минут 58 секунд:
Mirage
Уберите в потоке Sleep, и получите не всякое долгое дело, в итоге поток-счётчик займёт КС не давая никому к ней пробиться, не смотря на то, что постоянно выходит из неё и входит в неё, а другие уже давно сидят и ждут. Получается какой-то странный выход из КС без выхода.
Sasha
новенький
 
Сообщения: 41
Зарегистрирован: 07.12.2015 01:27:43

Re: Зависание потоков при использовании критических секции

Сообщение Mirage » 22.10.2017 18:37:13

Как сокет может быть общими данными? Разве что если несколько потоков туда пишут/читают, что едва ли приведет к чему-то хорошему.
Скорее всего, задача потока прочитать из сокета данные или записать их туда. В обоих случаях, ему нужно забирать крит. секцию только на время работы с общими данными, но не сокетом. Т.е. читаем из сокета, записываем в область памяти, известную всем заинтересованным потокам.

Эта статья не теория, а лишь статья по крит. секциям, причем виндовым и, скорее всего, уже устарела.
Впрочем, в ней не сказано, что другой поток получит крит. секцию первым, если он запросил её раньше.
Это свойство называется fairness. Сама по себе критическая секция такого свойства не имеет. Могут иметь другие примитивы. Например, TMonitor в Delphi, насколько я знаю, это свойство имеет. Но оно далеко не бесплатно, и, как правило, не нужно. Поэтому, по умолчанию его нет.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Зависание потоков при использовании критических секции

Сообщение olegy123 » 22.10.2017 19:01:47

Sasha писал(а):"Надо работать с общими данными" - А елси сокет и есть общие данные?
UDP? или TCP?

Добавлено спустя 4 минуты 10 секунд:
Если TCP - сокет неблокирующий?
olegy123
долгожитель
 
Сообщения: 1643
Зарегистрирован: 25.02.2016 12:10:20

Re: Зависание потоков при использовании критических секции

Сообщение скалогрыз » 22.10.2017 20:20:43

Sasha писал(а):Не до конца понял Ваш вариант с 2-я КС, но покумекаю может пойму.

то что не понял, это норм. Но он хотя бы работает?

Для понимания: TDoubleSC добавляет дополнительный уровенить изоляции для захвата критической секции.
Можно сказать так, что пытаться войти в критическую секцию может только один поток. (т.е. это как раз реализация той самой очереди из 1 потока).

И работает это в твоём примере, потому что, первый поток получив критическую секцию засыпает.
Второй поток, работая (пока первый делает Sleep(100)) изолирует основную критическую секцию (захватив fgate), но ожидая (fmain)
После этого поток-счётчик, попытавшись снова взять fmain, сначала будет ждать пока первый поток отработает (захватит fmain, освободит fgate)

Это то же самое, что ты пытаешься сделать, добавив for i:=1 to 5000, но без бесполезного расхода CPU.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Зависание потоков при использовании критических секции

Сообщение Sasha » 22.10.2017 20:57:06

olegy123 Забудьте по сокеты, речь не о них. UDP сокеты так же могут быть блокикующими и не блокирующими, выделять тут как TCP не стоит.

скалогрыз Тестовую прогу напишу, проверю.
Sasha
новенький
 
Сообщения: 41
Зарегистрирован: 07.12.2015 01:27:43

Re: Зависание потоков при использовании критических секции

Сообщение MysticCoder » 22.10.2017 21:19:39

Думаю, что проблему тут решать надо не танцами с бубном над существующим алгоритмом, а с постановкой изначальной задачи. Sasha, сформулируй подробнее какую именно задачу тебе надо решить. Наверняка, она решается намного проще, просто алгоритм сейчас выбран не совсем удачный.
MysticCoder
постоялец
 
Сообщения: 154
Зарегистрирован: 14.09.2013 00:20:28

Re: Зависание потоков при использовании критических секции

Сообщение Sasha » 22.10.2017 22:08:16

скалогрыз Попробовал Вашим способом, сначала подумал, что работает, но... Если просто смотреть на вывод, то всё нормально, но когда начинаешь двигать форму (если вместо Sleep сделать цикл от 0 до 10000), менять её размер, то счётчик начинает уходить от нулевого значения, это означает, что один из потоков запускается чаща другого, значит этот вариант не работает.
Вот код:
Код: Выделить всё
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ExtCtrls, syncobjs;

type

  { TMyCritSec }

  TMyCritSec=class
  private
    fmain:TRTLCriticalSection;
    fgate:TRTLCriticalSection;
  public
    constructor Creat;
    destructor Destroy; override;
    procedure Enter;
    procedure Leave;
  end;

  { TMyThread }

  TMyThread=class(TThread)
  protected
    delta:Integer;

    procedure Execute; override;
  end;


  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Memo1: TMemo;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { private declarations }
  public
    FTh1:TMyThread;
    FTh2:TMyThread;
  end;

var
  Form1: TForm1;
  Counter:Integer;
  MYCS:TMyCritSec;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  MYCS:=TMyCritSec.Creat;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Memo1.Append(Format('counter=%d',[Counter]));
end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  if not FTh1.Finished then
    begin
      FTh1.Terminate;
      FTh1.WaitFor;
    end;

  if not FTh2.Finished then
    begin
      FTh2.Terminate;
      FTh2.WaitFor;
    end;

  MYCS.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FTh1:=TMyThread.Create(True);
  FTh1.FreeOnTerminate:=True;
  FTh1.delta:=1;

  FTh2:=TMyThread.Create(True);
  FTh2.FreeOnTerminate:=True;
  FTh2.delta:=-1;

  FTh1.Start;
  FTh2.Start;

  Timer1.Enabled:=True;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  MYCS.Enter;

  Memo1.Append(Format('counter=%d',[Counter]));
  WriteLn(Format('Main Thread counter=%d',[Counter]));
  Sleep(3000);

  MYCS.Leave;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  WriteLn('close th1');

  FTh1.Terminate;
  FTh1.WaitFor;

  WriteLn('close th2');

  FTh2.Terminate;
  FTh2.WaitFor;
end;

{ TMyThread }

procedure TMyThread.Execute;
var
  i:Integer;
begin
  repeat
    MYCS.Enter;
    Counter:=Counter+delta;
    WriteLn(Format('counter=%d',[Counter]));
    //Sleep(1);
    i:=0; repeat Inc(i); until i=10000;
    MYCS.Leave;
  until CheckTerminated;
end;

{ TMyCritSec }

constructor TMyCritSec.Creat;
begin
  InitCriticalSection(fmain);
  InitCriticalSection(fgate);
end;

destructor TMyCritSec.Destroy;
begin
  DoneCriticalsection(fmain);
  DoneCriticalsection(fgate);
  inherited Destroy;
end;

procedure TMyCritSec.Enter;
begin
  EnterCriticalsection(fgate);
  EnterCriticalsection(fmain);
  LeaveCriticalsection(fgate);
end;

procedure TMyCritSec.Leave;
begin
  LeaveCriticalsection(fmain);
end;

end.



MysticCoder
Задача простая: есть 2 потока один увеличивает глобальную переменную, другой её уменьшает, основной поток выводит значение глобального счётчика по нажатию кнопки. Потоки должны выполняться только друг за другом т.е. если один поток уменьшил счётчик, сразу после этого должен запускаться другой поток (который ждал уменьшения), который увеличивает счётчик т.е. всякий раз когда выполняется чтение значение счётчика то оно не должно увеличиваться или уменьшаться.
Sasha
новенький
 
Сообщения: 41
Зарегистрирован: 07.12.2015 01:27:43

Re: Зависание потоков при использовании критических секции

Сообщение olegy123 » 22.10.2017 22:50:05

Sasha писал(а):Задача простая: есть 2 потока один увеличивает глобальную переменную, другой её уменьшает, основной поток выводит значение глобального счётчика по нажатию кнопки.
Ах..
Код: Выделить всё

var
  global_value:Integer;
  criticalsection:TCriticalsection;
  event:TEvent;


Thread #1
procedure  execute();
begin
while not terminate do begin
   criticalsection.Enter;
      Inc(global_value);
event.setevent();
   criticalsection.Leave;
  event.Wait();
   Sleep(100);
end;
end

Thread #2
procedure  execute();
begin
while not terminate do begin
   criticalsection.Enter;
      Dec(global_value);
   event.setevent();
   criticalsection.Leave;
  event.Wait();
   Sleep(100);
end;
end

кнопка()
   criticalsection.Enter;
      Caption:=Inttostr(global_value);
   criticalsection.Leave;
olegy123
долгожитель
 
Сообщения: 1643
Зарегистрирован: 25.02.2016 12:10:20

Re: Зависание потоков при использовании критических секции

Сообщение Sasha » 22.10.2017 23:05:12

olegy123 Это вы тут хорошо написали, но попробуйте сделать реальную программу и посмотрите как это будет работать. Только Sleep после выхода из КС не годится, зачем мне задержка потока, который после выхода из КС должен что-то полезное делать.
И я бы на Вашем месте посмотрел всё обсуждение, Ваш вариант решения уже был опробован и отклонён но не по причине неработоспособности, а именно из-за задержки, которую даёт Sleep.

Добавлено спустя 4 минуты 35 секунд:
Да, ещё, в основном потоке КС не обязателена, основной поток только читает значение счётчика, чтобы убедится, что счётчик не уходит в + или в -, для этого необязателен монопльный доступ к счётчику.
Sasha
новенький
 
Сообщения: 41
Зарегистрирован: 07.12.2015 01:27:43

Re: Зависание потоков при использовании критических секции

Сообщение MysticCoder » 22.10.2017 23:09:48

Sasha,
Код: Выделить всё
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, syncobjs;

type

  { TWorkThread }

  TWorkThread = class(TThread)
    Delta : integer;
    WorkEvent : TEvent;
    WorkCompleteEvent : TEvent;
    constructor Create(ADelta: integer; AWorkEvent : TEvent; AWorkCompleteEvent : TEvent; ASuspended: Boolean=True);
    procedure Execute; override;
  end;

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;
  Counter : integer;
  CounterSection : TCriticalSection;
  PlusThread, MinusThread : TWorkThread;
  PlusEvent, MinusEvent : TEvent;
implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  Counter := 50;
  CounterSection := TCriticalSection.Create();
  PlusEvent := TEvent.Create(nil, False, False, '');
  MinusEvent := TEvent.Create(nil, False, False, '');
  PlusThread := TWorkThread.Create(1, PlusEvent, MinusEvent, True);
  MinusThread := TWorkThread.Create(-1, MinusEvent, PlusEvent, True);
  PlusThread.Start();
  MinusThread.Start();
  PlusEvent.SetEvent();
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  CounterSection.Enter();
  Caption := IntToStr(Counter);
  CounterSection.Leave();
end;

{ TWorkThread }

constructor TWorkThread.Create(ADelta: integer; AWorkEvent: TEvent;
  AWorkCompleteEvent: TEvent; ASuspended: Boolean);
begin
  Delta := ADelta;
  WorkEvent := AWorkEvent;
  WorkCompleteEvent := AWorkCompleteEvent;
  inherited Create(ASuspended);
end;

procedure TWorkThread.Execute;
var
  xWaitResult : TWaitResult;
begin
  while not Finished do
    begin
      xWaitResult := WorkEvent.WaitFor(1);  // ждем разрешения на работу
      if xWaitResult <> wrSignaled then
        Continue;
      CounterSection.Enter();
      Counter := Counter + Delta;
      CounterSection.Leave();
      WorkCompleteEvent.SetEvent();         // рапортуем об окончании работы > другой поток может начинать работу
    end;
end;

end.
MysticCoder
постоялец
 
Сообщения: 154
Зарегистрирован: 14.09.2013 00:20:28

Re: Зависание потоков при использовании критических секции

Сообщение скалогрыз » 22.10.2017 23:15:51

Sasha писал(а): Если просто смотреть на вывод, то всё нормально, но когда начинаешь двигать форму (если вместо Sleep сделать цикл от 0 до 10000), менять её размер, то счётчик начинает уходить от нулевого значения, это означает, что один из потоков запускается чаща другого, значит этот вариант не работает.

ты как-то быстро поменял условия задачи.
Сначала же один поток, не? я же тебе писал, что очередь из 1-го потока, а не двух.
Ты можешь быстро решить проблему, добавив ещё один гейт в свою критическую секцию:
Код: Выделить всё
procedure TMyCritSec.Enter;
begin
  EnterCriticalsection(fgate2);
  EnterCriticalsection(fgate);
  LeaveCriticalsection(fgate2);
  EnterCriticalsection(fmain);
  LeaveCriticalsection(fgate);
end;

но, опять же, настоящая проблема у тебя идиологическая -> ты почему ты пытаешься привязать синхронизацию служебных потоков к решению задач в основном потоке.
(ну т.е. ты решаешь, что основной поток, может залочить исполнение служебных всерьёз и надолго.), а вот такой подход, превращает многопоточную систему в однопоточную.
все-же друг-друга ждут... ну и какой смысл иметь много потоков, если все друг-друга дожидаются и выполняют задачи последовательно?! где параллелизм?
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Зависание потоков при использовании критических секции

Сообщение Sasha » 22.10.2017 23:25:53

MysticCoder Попрубую Ваш вариант. Правда решение для Линкуса нужно, но думаю можно его перетащить на Линукс.

скалогрыз Очередь из одного потока, потому как один работает (т.е. не стоит в очереди), другой ждёт в очереди, т.е. длина очереди 1.
По поводу изменений условий, я бы так не сказал, просто изначально основной поток был одним из тех 2-х, но потом он стал просто наблюдателем.
Sasha
новенький
 
Сообщения: 41
Зарегистрирован: 07.12.2015 01:27:43

Пред.След.

Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 32

Рейтинг@Mail.ru