-
Вот такая проблема случилась: Пишу подобие сокс5-сервера, с сипользование winapi-потоков, т.е. при старте запускаеться отдельный поток в котором крутиться бесконечный цикл с Accept, и как только есть подключившийся сокет, он создает поток и передает этот сокет туда... Так вот странность какая, что вродебы никаких общих переменных нету, никакой синхронизации нету(за её ненадобностью), т.е. все потоки существуют отдельно друг от друга. Так вот откудато берется ошибка памяти ((
В потоке примерно вот такая штука крутиться:
while True do
begin
FD_Zero(FDSet);
FD_Set(srv_sock,FDSet);
Select(0,@FDSet,nil,nil,@timeout);
if FD_IsSet(srv_sock,FDSet) then
begin
accepted_sock:=Accept(srv_sock,@Addr,@addr_size);
CreateThread(nil, 0, @cli_thread, pointer(accepted_sock), 0, h);
end;
end;
Так вот кстати, как правельно разрушать потоки???
изнутри ExitThread(0); недостаточно? или как это правельно вообще делаеться?
-
> откудато берется ошибка памяти
Она "берется" из 17-й строки в твоем коде
> изнутри ExitThread(0); недостаточно?
Достаточно.
Иногда даже излишне.
-
> вродебы никаких общих переменных нету
а это по твоему не общая: accepted_sock
-
> синхронизации нету(за её ненадобностью)
Смелое заявление)
-
>
> Незнайка Винидиктович (29.03.09 18:25)
Тебе делать надо как то так:
ClientSocket := accept(ListenSocket, nil, nil);
if ClientSocket <> INVALID_SOCKET then
begin
New(Info);
Info^.Socket := ClientSocket;
hClientThread := BeginThread(nil, 0, @SocketThread, Info, 0, ThreadId);
....
где Info: PThreadData;
ThreadData = record
Socket: TSocket;
.....
end;
PThreadData = ^ThreadData;
-
> а это по твоему не общая: accepted_sock
В коде выше - не общая.
> Тебе делать надо как то так:
Зачем усложнять? Он передает одну переменную, которая равна (по размеру) указателю (SOKET = HANDLE = LPVOID).
> Вот такая проблема случилась
А в каком месте ошибка то происходит?
-
> > откудато берется ошибка памяти
>
>
> Она "берется" из 17-й строки в твоем коде
хм. в представленом мной коде всего 13 строк.
> > изнутри ExitThread(0); недостаточно?
>
> Достаточно.
> Иногда даже излишне.
Спасибо за ответ! )
> а это по твоему не общая: accepted_sock
В данном случае accepted_sock не общая, помойму вы что-то путаете...
> > Вот такая проблема случилась
>
> А в каком месте ошибка то происходит?
Вот даже и незнаю, нету ни обших переменных ничего такого что должно было бы её вызвать и как оказалось завершаю потоки(ExitThread(0);) тоже правельно, но она подло откудато вылезает. Вылетает она в разных местах всегда, как не пытался дебагить, ничего не получилось, да и все ещё ослажняеться тем, что потоков несколько... а вообще откуда она может взяться если нету общих ресурсов ?
Я бы сюда полный листинг вывел, да пишет что ограничение по символам ((
-
ЛЮДИ! всем спасибо за ответы, проблема обнаружена!
Это было просто переполнение буфера, в самом клиентском потоке ))
Ещё раз всем спасибо!
-
> мимо (29.03.09 22:36) [5]
>
>
> > а это по твоему не общая: accepted_sock
>
> В коде выше - не общая.
неправда, ты передаешь указатель в поток на переменную, дальнейшая судьба которой в основном потоке неизвестна (по крайней мере из приведенного кода)
-
> > мимо (29.03.09 22:36) [5]
Как только произойдет ВТОРОЙ коннект к серверу, значение этой переменной будет переписано новым значением и кирдык.
-
> Незнайка Винидиктович (29.03.09 18:25)
Еще, ты решил использовать CreateThread вместо BeginThread, надеюсь, не забыл про выставление флага IsMultiThread := TRUE;
-
> мимо (29.03.09 22:36) [5]
>
>
> Зачем усложнять? Он передает одну переменную, которая равна
> (по размеру) указателю (SOKET = HANDLE = LPVOID).
Можно и не усложнять. Просто так ему будет потом удобнее, если еще что то добавить в передаваемые параметры. Я хотел бы обратить внимание, что он всегда передает указатель на одну и ту же переменную, которую потом перезаписывает при каждом новом коннекте.
-
Но встал другой вопрос. С потоками все впорядке, создаються-завершаютьсьяс все ок, но вот размер приложения в процессе работы растет, из-за чего это может быть?
-
Я имел ввиду размер в памяти... (
> > мимо (29.03.09 22:36) [5]> > > > а это по твоему не
> общая: accepted_sock> > В коде выше - не общая.неправда,
> ты передаешь указатель в поток на переменную, дальнейшая
> судьба которой в основном потоке неизвестна (по крайней
> мере из приведенного кода)
так там все просто происходит
localpar:=lpParam;
т.е. легким движением руки она превращаеться в локальную переменную
-
> Я хотел бы обратить внимание, что он всегда передает указатель
> на одну и ту же переменную, которую потом перезаписывает
> при каждом новом коннекте.
Где там указатель? Там приведение типа SOCKET к типу Pointer. Никакого указателя там нет.
И ничего он не перезаписывает. Внимательно на код посмотрите.
-
> размер приложения в процессе работы растет
Где-то утечка памяти.
-
> > Я хотел бы обратить внимание, что он всегда передает указатель
> > на одну и ту же переменную, которую потом перезаписывает
> > при каждом новом коннекте.Где там указатель? Там приведение
> типа SOCKET к типу Pointer. Никакого указателя там нет.И
> ничего он не перезаписывает. Внимательно на код посмотрите.
>
Совершенно верно
-
> > размер приложения в процессе работы растетГде-то утечка
> памяти.
Извините конечно за глупость, но как её искать? )
-
> мимо (30.03.09 00:04) [14]
> Где там указатель? Там приведение типа SOCKET к типу Pointer.
> Никакого указателя там нет.
Да все верно, че-то я перепутал. Мне почему то казалось что там написано типа такого: pointer(@accepted_sock)
Уж не знаю, с чего я так решил.
-
> размер приложения в процессе работы растет
Не факт, что утечка, где смотрите размер занимаемой памяти? В диспетчере задач? Если да, то в каком столбце? Лучше взять что-то вроде MemProof и смотреть там, покажет все утечки вплоть до строк в коде (если его правильно настроить)
-
> Не факт, что утечка, где смотрите размер занимаемой памяти?
> В диспетчере задач? Если да, то в каком столбце? Лучше
> взять что-то вроде MemProof и смотреть там, покажет все
> утечки вплоть до строк в коде (если его правильно настроить)
Да в диспетчере задач. смотрю в колонку память ) Предложенный софт сейчас попробую, может что выйде из этого
-
MemProof тоже показывает утечку, т.е. текущие и пиковые значения примерно равны... но как найти утечку пока не понял, вообщем в процессе поиска ))
-
> Да в диспетчере задач. смотрю в колонку память )
Смотри Виртуальную память, но все равно это не точно и неудобно, лучше использовать спец инструменты.
-
> Незнайка Винидиктович (30.03.09 00:49) [21]
Можно для начала воспользоваться не MemProof, а FastMM4.
-
Ну можно вообще просто считать вызовы GetMem/FreeMem (переопределить указатели на эти функции - это самое простое).
-
> мимо (30.03.09 01:48) [24]
>
> Ну можно вообще просто считать вызовы GetMem/FreeMem (переопределить
> указатели на эти функции - это самое простое).
>
Хм. Это уже мания,
Имхо. :)
-
> в представленом мной коде всего 13 строк
А никто и не говорил что проблема в представленном коде.
Проблема - в одной из 17-х строк в коде , который ты не представил.
-
> > в представленом мной коде всего 13 строкА никто и не говорил
> что проблема в представленном коде.Проблема - в одной из
> 17-х строк в коде , который ты не представил.
безусловно это так, но весь код немогу вставить тут ограничение )) ну вообщемто проблема сейчас уже в другом (
-
> Ну можно вообще просто считать вызовы GetMem/FreeMem (переопределить
> указатели на эти функции - это самое простое).
Так я вообще в своем приложении использовал только переменные интеджер и стринг впринципе...
-
> весь код немогу вставить
Не надо ничего никуда "вставлять".
Потенциальный источник проблем :
> никакой синхронизации нету(за её ненадобностью)
Флаг IsMultiThread имеет прямое отношение к синхронизации.
-
> использовал .. стринг
Ну вот тебе и грабли, как и просил)
-
> > весь код немогу вставитьНе надо ничего никуда "вставлять".
> Потенциальный источник проблем :> никакой синхронизации
> нету(за её ненадобностью)Флаг IsMultiThread имеет прямое
> отношение к синхронизации.
Как я понимаю(при моём скромном опыте программирования), что синхронизация нужна только в том случае если потоки обращаютсья одновременно к общим ресурсам, так?
-
Так.
Ресурсы BMM (Borland Memory Manager в составе Run-Time Library) как раз и есть общие.
Работая с длинными строками ты неявно обращаешься к BMM и, соотв-но, к его ресурсам.
Установка глобального флага IsMultiThread предписывает BMM при помощи крит.секции выстраивать в очередь явные или неявные запросы к нему со стороны потоков прикладного кода, это и есть та самая синхронизация, в которой у тебя якобы нет необходимости.
Этот же флаг неявно взводится при вызове BeginThread, кр.того BeginThread делает еще несколько крайне полезных действий, направленных, например, на корректную и удобную обработку неперехваченных тобой исключений, потенциально могущих возникать в теле поточной функции.
-
> Так.Ресурсы BMM (Borland Memory Manager в составе Run-Time
> Library) как раз и есть общие.Работая с длинными строками
> ты неявно обращаешься к BMM и, соотв-но, к его ресурсам.
> Установка глобального флага IsMultiThread предписывает BMM
> при помощи крит.секции выстраивать в очередь явные или неявные
> запросы к нему со стороны потоков прикладного кода, это
> и есть та самая синхронизация, в которой у тебя якобы нет
> необходимости.
Немного в шоке оО
Т.е. даже если эта строка локальная в поточной функции с помощью какого-то BMM евляеться общей ??
нет я даже много в шоке )
-
Ну впринципе сейчас попробую переделать на БегинТред, посмотрим что из этого выйдет.
-
хм. в BeginThread заголовок вроде идентичен с CreateThread, а вот параметр для функции некорректно воспринимает
-
> строка локальная в поточной функции с помощью какого-то
> BMM евляеться общей ?
Да не строка общая, а ресурсы самого BMM !
Обращение к этим ресурсам происходит в момент неявных вызовов Get/Realloc/FreeMem, потенциально происходящих при использовании длинных строк. Причем фиолетово, локальные ли эти строковые переменные или не локальные.
-
> параметр для функции некорректно воспринимает
Читай текст ошибки, вникай, исправляй ошибку
-
> > параметр для функции некорректно воспринимаетЧитай текст
> ошибки, вникай, исправляй ошибку
Да ошибки то как таковой нету, просто значения в поток передаеться вместо одного другое (( хотя все по мануалу делаю (
-
accepted_sock:=Accept(srv_sock,@Addr,@addr_size);
BeginThread(nil, 0, @cli_thread, pointer(accepted_sock), 0, h);
а в потоке так:
procedure cli_thread(param: pointer); stdcall;
var
cli_sock: integer;
......
begin
cli_sock:=integer(param);
......
так вот работало с CreateThread. с бегинтред нихочет, даже делал по ману через указатель и передачу адреса, всеравно нето (
-
т.е. в cli_sock нето значение получаеться
-
> procedure cli_thread(param: pointer); stdcall;
function cli_thread(hClientSocket: TSocket): Integer; //никаких stdcall !!
var
cli_sock: TSocket;
begin
cli_sock := hClientSocket;
..
end;
-
> Незнайка Винидиктович (30.03.09 10:21) [39]
> accepted_sock:=Accept(srv_sock,@Addr,@addr_size);
> BeginThread(nil, 0, @cli_thread, pointer(accepted_sock),
> 0, h);
>
> а в потоке так:
>
> procedure cli_thread(param: pointer); stdcall;
> var
> cli_sock: integer;
> ......
> begin
> cli_sock:=integer(param);
> ......
>
>
> так вот работало с CreateThread. с бегинтред нихочет, даже
> делал по ману через указатель и передачу адреса, всеравно
> нето (
function cli_thread(param: pointer):Integer; - A stdcall надо убрать;
Смотрим хелп по BeginThread
type TThreadFunc = function(Parameter: Pointer): Integer;
-
Люди! Списибо с параметрами теперь все ок!
Теперь надо найти из-за чего увеличиваеться(и потом не возвращается обратно) размер приложения в памяти.
Зделал просто ради экспиремента:
function func(param: pointer): integer;
var
n: integer;
begin
n:=integer(param);
EndThread(0);
end;
.......
for t:=0 to 1000 do BeginThread(nil, 0, @func, pointer(t), 0, h);
Потоки рождаються и умирают нормально. Но вот примерно через
12K отработавших потоков размер памяти увеличиваеться на
4КБ, нормально это или нет(тупо незнаю из-за малого опыта программирования)???
А вот немного переделал, тоже ради экспиримента:
function func(param: pointer): integer;
var
n: integer;
buf: string;
begin
n:=integer(param);
buf:=IntToStr(n);
EndThread(0);
end;
..........
for t:=0 to 1000 do BeginThread(nil, 0, @func, pointer(t), 0, h);
Так вот теперь через каждые
1К отработавших потока, размер увеличиваеться на
16КБ, что это значит?
Извините, если это откровенная глупость...
-
> Теперь надо найти из-за чего увеличиваеться(и потом не возвращается
> обратно) размер приложения в памяти.
Ты хендлы закрываешь?
BeginThread возвращает хэндл, какова его дальнейшая судьба у тебя?
-
> Незнайка Винидиктович (30.03.09 17:08) [43]
Утечка происходит как минимум из-за незакрытия тобой хэндлов создаваемых потоков.
> buf:=IntToStr(n);
>
Вот как раз эти граблища рано или поздно больно хлобыстнули бы тебя, если бы IsMultiThread был равен False
-
> Утечка происходит как минимум из-за незакрытия тобой хэндлов
> создаваемых потоков.
Т.е. EndThread(0); в потоке не достаточно? Нужно обязательно закрывать хедл снаружи? изнутри никак чтоли?
-
> Т.е. EndThread(0); в потоке не достаточно? Нужно обязательно
> закрывать хедл снаружи? изнутри никак чтоли?
Даже если так то, почему такая разница между двумя примерами?
-
> изнутри никак чтоли?
Чтоли
> почему такая разница между двумя примерами?
Утечка не здесь
-
> Т.е. EndThread(0); в потоке не достаточно? Нужно обязательно
> закрывать хедл снаружи? изнутри никак чтоли?
Поток он сам по себе, а его хендл сам по себе. Поток ты уничтожил, но хендл надо закрыть CloseHandle()
-
> > изнутри никак чтоли?Чтоли
Что из нутри никак это плохо (( Что за всеми потоками следить чтоли?
> > почему такая разница между двумя примерами?Утечка не здесь
Так а где если не здесь, все что есть в этих двух вариантах все тут...
-
> Что за всеми потоками следить чтоли?
>
Не надо - не следи, никто не заставляет.
Получил хэндл, тут же закрыл его и забыл про него как страшный сон)
> через 12K отработавших потока
> через каждые 1К отработавших потока
Как ты узнал что они отработали ?
У тебя ж никакой синхронизации нет , "за ненадобностью" ..
-
> Не надо - не следи, никто не заставляет.Получил хэндл, тут
> же закрыл его и забыл про него как страшный сон)
Т.е. можно закрыть хедл и работать в потоке дальше, это не как не повлият на работу потока ?
> > через 12K отработавших потока
> > через каждые 1К отработавших
> потокаКак ты узнал что они отработали ?У тебя ж никакой
> синхронизации нет , "за ненадобностью" ..
Узнал смотря в диспетчер задач и в дебаггер в самой делфи, это не достоверно?
-
> > > почему такая разница между двумя примерами?Утечка не
> здесьТак а где если не здесь, все что есть в этих двух вариантах
> все тут...
Вопрос так и не нашёл ответа ? )
-
т.е. из предложенных двух примеров не видно где утечка ?
-
> это не как не повлият на работу потока ?
Никак.
> из предложенных двух примеров не видно где утечка ?
В них при IsMultiThread=True утечки нет.
-
> > из предложенных двух примеров не видно где утечка ?В них
> при IsMultiThread=True утечки нет.
Ну так я там использую бегинТред, значит IsMultiThread=True, следовательно утечки не должнобыть по вашим словам, но тем не мение...
-
Приводи полный код ..
-
так выше представленный код и есть полный... больше там ничего нету
это чисто экспиримент
-
> так выше представленный код и есть полный
Так а какого же лешего ты не внял рекомендациям по закрытию хэндлов ?
-
Внял )) Но как ты говорил что утечка не из-за них ))
-
так у ThreadId тип cadinal а не обжект? а CloseHandle(тут обжект) ... ну так это не обязатльно ты же говорил что не из-за этого утечка ?
-
ООочень странная ситуация получаеться.
в примере(см.выше) делаем так:
function func(param: pointer): integer;
var
n: integer;
buf: string;
begin
n:=integer(param);
buf:=IntToStr(n);
Func:=0;
end;
Т.е. убераем EndThread(0); и память не увеличивается ))
вообще магия какая-то, вот тебе и течка памяти )
-
Хедл так и не понял как закрывать, и вообще зачем, это переменная, тем более она перезаписавается, что с неё будет ?
-
> у ThreadId тип cadinal а не обжект? а CloseHandle(тут обжект)
Причем тут ThreadId ?
Это глобально уникальный идентификатор треда, а не его хэндл.
CloseHandle требует параметром хэндл треда, а не его идентификатор.
> убераем EndThread(0); и память не увеличивается ))
> вообще магия какая-то
"Магия" тут очень простая - вызывав EndThread ты потребовал от системы немедленного и безусловного завершения работы текущего треда, в то время как фактическая финализация переменной buf (читай - освобождение памяти, распределенной под строковые данные при IntToStr) произойдет лишь при любом трех условиях:
1. Выполнение оператора Exit
2. Выполнение оператора End
3. Необработанная в теле поточной ф-ции исключительная ситуация.
-
Как я уже написал выше, что без exitthread или endthread память как оказалось утекает меньше намного(почти не утекает), а с exitthread или endthread утекает ваще жостко, наверно решение где-то здесь?
Полность утечку не удалось прекратить ((
-
> что без exitthread или endthread память как оказалось утекает
> меньше намного(почти не утекает), а с exitthread или endthread
> утекает ваще жостко, наверно решение где-то здесь?
Кто тебя вообще научил exitthread использовать. Поток должен умирать сам или по указке извне, но тоже сам. Т.е. поток должен периодически проверять флаг ПОРА ЗАКРУГЛЯТЬСЯ и если он ИСТИНА, то поток подчищаетвсе свои ресурсы и закругляется. Это единственный правильный способ. Все остальные способы будут приводить к утечкам.
-
Про Handle потока, оторый следует закрывать:
Handle есть описатель (читай указатель) на некую служебную информацию, которая система использует для управления потоком. Для каждого объекта (в данном случае потока) система ведет счетчик ссылок. Пока сей счетчик не равен 0 система не уничтожит (не освободит память) объект "поток" (структуру данных в памяти). Счетчик ссылок увеличивается при создании объекта (становится равным 1) и при вызове некоторых функций - таких как DuplicateHandle и уменшается при закрытии описателей. Соответственно, если не вызвать CloseHandle и не закрыть описатель потока - память не будет освобождена (от отбъекта "поток") до тех пор, пока не завершится все приложение (процесс). По сему, если этот описатель нигде больше не использется его следует сраз закрыть.
О завершении потоков:
Поток завершается вызовом функции ExitThread, TerminateThread либо выходом из функции потока (который приводит к вызову ExitThread с кодом завершения 0). Т.е. если явно не вызывать ExitThread то она будет вызвана после выполнения функции потока.
Важный момент: ExitThread НЕ возвращает управления вызывающей процедуре. Как следствие, в вашем примере, переменные типа string никогда не будут уничтожены (память, отведенная под них). Так как они являются управляемыми. Т.е., другими словами, "эпилог" функции не будет выполнен и локальные управляемые переменные не уничтожатся.
-
> Хедл так и не понял как закрывать
closehandle(то что ты получил результатом вызова BeginThread)
> и вообще зачем, это переменная
Какая еще нахрен переменная ?
Closehandle(BeginThread(..)) - это даже ежу понятно..
-
> Closehandle(BeginThread(..)) - это даже ежу понятно..
Очень умно. А если поток не удалось создать?
-
> мимо (05.04.09 03:08) [69]
и что же будет если поток не удалось создать?
-
Как минимум - будет логическая ошибка, при которой программа преполагает запушенный поток, которого не будет.
-
А как максимум?
-
А как максимум - советую для разнообразия подумать самому. Мыслительный процесс он иногда полезен (по крайней мере - некоторым).
-
То есть ты не знаешь. )
-
Если Вас это успокоит и даст Вам возможность спать спокойно и с достоинством носить голубенький (С) - считайте, что я не знаю.
-
> мимо (05.04.09 03:08) [69]
Ну не удалось и не удалось. Катастрофы не случится.
Автора пока заботят утечки, а не "удачность запуска потока"