Форум: "Основная";
Текущий архив: 2005.01.30;
Скачать: [xml.tar.bz2];
ВнизВиснет программа! CriticalSection или ? Найти похожие ветки
← →
Miralex © (2005-01-18 11:54) [0]Мастера помогите разобраться почему моя программа зависает.
Использую я свой класс в программе. В классе я перекрываю критическими секциями все места, где возможно выполение из потока! В итоге почемуто моя программа виснет. Помогите разобраться.
Кусочек кода, на который я грешу:
procedure TCharacter.GameSendSHex(SHex : String; Encrypt : boolean);
var I, Len: Integer;
BufIn, BufOut : PArray;
begin
SocketSection.Enter;
if not GameClient.Active then
begin
SocketSection.Leave;
Exit;
end;
New(BufIn);
New(BufOut);
Len := Length(SHex);
{Конвертируем SHex в Array of byte}
if (Len = 0) or (Len mod 2 <> 0) then
begin
AddtoDebugMemo("Error converting SHex to Array of byte");
SocketSection.Leave;
Exit;
end;
for I := 0 to ((Len div 2)-1) do BufIn^[I] := StrToInt("$"+Copy(SHex, I*2+1, 2));
Len := Len div 2;
{Криптуем методом BlowFishCrypt если Encrypt=true и EncryptMetod=2}
if Encrypt and (EncryptMetod = 2) then BlowfishEncrypt(BlowfishCryptObj,BufIn,BufOut,Len)
else for i := Low(BufOut^) to High(BufOut^) do BufOut^[i]:=BufIn^[i];
{Пытаемся отослать байтики серверу}
if GameClient.Socket.Connected then
begin
try
GameClient.Socket.SendBuf(BufOut^,Len);
AddtoDebugMemo("Client send "+inttostr(Len)+" bytes: "+SHex);
except
AddtoDebugMemo("Error in Socket.SendBuf");
end;
end
else AddtoDebugMemo("You try send some bytes, but Character is not Connected");
SocketSection.Leave;
Dispose(BufIn);
Dispose(BufOut);
end; {End TCharacter.GameSendSHex}
procedure TCharacter.SendUnicodeText(Value : String);
var i : Integer;
S,SHex : String;
begin
S := Value;
{Если в S пусто то выходим}
if Length(S)=0 then Exit;
{в S - то что мы хотим сказать серверу}
SHex := "";
{Дописываем в конец 2 нуля}
SHex := "0000";
{Вставляем текст в формате "00" буква "00" буква и тд}
for i:=Length(s) downto 1 do SHex := "00" + IntToHex(ord(S[i]),2) + SHex;
{Добавляем в начало язык (4 байта)}
{Покачто только русский RUS="52 55 53"}
SHex := "52555300"+SHex;
{Добавляем тип(1 байт), цвет(2 байта) и шрифт(2 байта)}
SHex := "0002B20003"+SHex;
{Длина пакета}
SHex := IntToHex((Length(SHex) div 2)+3,4) + SHex;
SHex := "AD" + SHex;
GameSendSHex(SHex,true);
end;
Из потока я вызываю так:
TCharacter(Char).SendUnicodeText(S);
Критических секций в потоке нету, так как они внутри класса перекрывают все! Насколько я понял так можно их использовать. Или нет?
В итоге программа зависает на N-ом использовании TCharacter(Char).SendUnicodeText(S) из потока.
Думал что гдето зацикливаються критические секции - поубирал все что неиспользую в данный момент. Всеравно виснет.
Мастера, подскажите пожалуйста где ошибка?
← →
TUser © (2005-01-18 12:14) [1]ИМХО, тут гораздо лучше использовать Synchronize.
> Критических секций в потоке нету, так как они внутри класса
> перекрывают все!
То, что в одном потоке уже есть критические секции вовсе не означает, что в другом потоке их не должно быть. Точнее - означают как раз обратное.
← →
Miralex © (2005-01-18 12:23) [2]Да но по сути метод Synchronize достаточно медленнее чем CriticalSection.
> ИМХО, тут гораздо лучше использовать Synchronize.
Почему лучше?
← →
Digitman © (2005-01-18 12:36) [3]
> Miralex
код твой никуда не годится.
огромная куча несуразностей, некорректностей и т.д. и т.п.
переписывать с нуля его нужно.
← →
Miralex © (2005-01-18 12:40) [4]
> огромная куча несуразностей, некорректностей и т.д. и т.п.
где например?
← →
Digitman © (2005-01-18 12:46) [5]начнем с того, что обращение к крит.секции следует оформлять в виде
КС.Enter;
try
.. что-то там, не важно что ..
finally
КС.Leave;
end;
← →
Miralex © (2005-01-18 12:49) [6]
> Digitman
А критическую секцию можно использовать внутри процедур или же надо использовать опасную процедуру внутри критической секции?
← →
TUser © (2005-01-18 12:52) [7]
> А критическую секцию можно использовать внутри процедур
Можно. TThreadList так и делает, например.
← →
Miralex © (2005-01-18 12:56) [8]Я так и не могу понять как мне надо делать?
Понаходить в коде все обращения GameSendSHex(SHex,true) и поменять на
SocketSection.Enter;
try
GameSendSHex(SHex,true);
finally
SocketSection.Leave;
end;
И так сделать для всех процедур, которые я использую из потока?
← →
Digitman © (2005-01-18 13:11) [9]
> Miralex © (18.01.05 12:49) [6]
крит.секцию можно использовать где угодно
> И так сделать для всех процедур, которые я использую из
> потока?
крит.секциями защищаются не процедуры, а программные ресурсы
у тебя, очевидно, есть как минимум один программный ресурс, который следует защищать - это объект класса TClientSocket.. он у тебя выполняет ф-ции единого трансп.канала для всех твоих трэдов, и одновременное обращение к этому объекту со стороны более чем одного трэда недопустимо
т.е. в ДАННОМ контексте в блок аправления крит.секцией достаточно включить лишь обращение к объекту GameClient, и то при условии что он работает в режиме ctBlocking .. при ctNonBlocking же код ощутимо усложняется, такая простейшая защита в этом случае никуда не годится из-за асинхронности работы гнезда
← →
Miralex © (2005-01-18 13:18) [10]
> Digitman
а можно по подробнее для режима NonBlocking...
и еще вопрос:
Что в данном случае лучше и для чего использовать.. Мне надо защищать как и переменные так и сокет соединение.
← →
Digitman © (2005-01-18 13:39) [11]
> можно по подробнее для режима NonBlocking
этот вопрос д.б. задан отдельно в разделе "Сети"
> Мне надо защищать как и переменные
какие переменные конкретно ? покажи пальцем на примере упомянутого тобой кода ..
← →
Miralex © (2005-01-18 14:16) [12]Полно переменных которые мне необходимо будет использовать и потоке. Ну например:
property GameStarted : boolean read fGameStarted;
она мне нужна и в потоке.. причем в основной программе я буду и записывать в нее и читать из нее! в Потоке же мне необходимо будет только читать значение из нее.
я думал использовать критическую секцию.
в программе переписать на
property GameStarted : boolean read GetGameStarted;
ф-цию GetGameStarted описать так:
begin
GameStartedSection.Enter;
Result := fGameStarted;
GameStartedSection.Leave;
end;
в классе поменять везде где чтение на GetGameStarted и где запись поменять на SetGameStarted(Value : Boolean).
при этом процедуру SetGameStarted сделать с критической секцией по аналогии GetGameStarted!
Так правильно?
← →
Digitman © (2005-01-18 14:22) [13]
> Miralex © (18.01.05 14:16) [12]
навскидку - да, правильно.
только GameStarted - это не переменная, это св-во класса
а fGameStarted - это член (поле) класса
← →
Digitman © (2005-01-18 14:27) [14]
> AddtoDebugMemo
что здесь, в теле этого метода, творится ? ввызываются методы/св-ва некоего Memo ?
делать это в доп.трэде недопустимо.
← →
Miralex © (2005-01-18 14:32) [15]а что делать тогда для свойств класса, значение которых я буду менять из потока? дело в том что когда значение меняеться я генерирую соответствующее событие и значение отображаеться на форме. Ничего страшного не будет например если я сделаю так:
property CharName : String read GetCharName write SetCharName;
.....................................
{----------------------CharName---------------------}
function TCharacter.GetCharName:String;
begin
CharNameSection.Enter;
Result := fCharName;
CharNameSection.Leave;
end;
procedure TCharacter.SetCharName(Value:String);
begin
CharNameSection.Enter;
fCharName := Value;
//Send Event
if Assigned(EventOnCharNameChanged) then EventOnCharNameChanged(Self,Value);
CharNameSection.Leave;
end;
и например в потоке я просто напишу:
TCharacter(Char).CharName:="Name1";
ошибок не будет?
← →
Miralex © (2005-01-18 14:34) [16]
> что здесь, в теле этого метода, творится ? ввызываются методы/св-ва
> некоего Memo ?
Береться значение и добавляеться в конец TMemo, который на форме!
Необходимо использовать в потоке через Sincronize?
← →
Digitman © (2005-01-18 14:39) [17]
> и например в потоке я просто напишу:
> TCharacter(Char).CharName:="Name1";
>
> ошибок не будет?
не будет.
ты же защитил поле fCharName кр.секцией !
только один трэд в каждый момент времени будет иметь к нему доступ
> Береться значение и добавляеться в конец TMemo, который
> на форме!
> Необходимо использовать в потоке через Sincronize?
да, синхронизация обязательна.
класс TMemo - в составе VCL, а все виз.объекты VCL в подавляющем большинстве прогр.случаев требуют синхронизацию обращений к ним с осн.трэдом
← →
Miralex © (2005-01-18 14:42) [18]
> > и например в потоке я просто напишу:
> > TCharacter(Char).CharName:="Name1";
> >
> > ошибок не будет?
>
>
> не будет.
Здесь в событии EventOnCharNameChanged() у меня также пишеться новое значение на форме! и что делать? тоже Sincronize?
← →
Digitman © (2005-01-18 14:50) [19]
> Miralex © (18.01.05 14:42) [18]
да, синхронизация и здесь будет также необходима.
← →
Miralex © (2005-01-18 14:55) [20]значит как я понял CriticalSection я могу использовать если надо защитить переменную!.. а если я хочу еще и VCL и тд - то только Sincronize?
← →
Digitman © (2005-01-18 15:13) [21]
> значит как я понял CriticalSection я могу использовать если
> надо защитить переменную
не совсем так.
крит.секцией ты можешь защишать все что подконтрольно тебе как разработчику кода.
в случае же с VCL ее внутренний код ничего не знает ни о каких твоих кр.секциях, поэтому чуть ли не единственный способ защититься от отказов, связанных с мультипоточным доступом к объектам VCL - осуществлять этот доступ в одном и том же (основном) трэде, что как раз и реализует синхронизация с использованием метода TThread.Synchronize
← →
Miralex © (2005-01-18 15:30) [22]понял. Большое спасибо.
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2005.01.30;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.166 c