Конференция "Сети" » CreateThread(), CriticalSection, SOCKET() [D6, D7, Win2k, WinXP]
 
  • 0xFFFF (15.10.12 19:17) [0]
    Всем привет!

    Вот примерно как-то так:


    function work_thread(param: pointer): dword; stdcall;
    begin
    while NOW_WORK=TRUE do
    begin
     EnterCriticalSection(CS);
      <читаем из глобальных переменных в локальные переменные потока>
     LeaveCriticalSection(CS);

     if (<проверяем условия>) then
     begin
      <работа>
     end else
     begin
      EnterCriticalSection(CS);
      NOW_WORK:=FALSE;
      LeaveCriticalSection(CS);
     end;
    end;

    Result:=0;
    end;



    Потоки рождаются и крутятся в цикле, пока не кончаться входные данные, либо не будет NOW_WORK=FALSE. Всё очень прекрасно работает(без сокетов), как дело доходит до сокетов(все переменные локальные sock, TSockAddr итд). Т.е. печалька в том, что если внутрь цикла поместить хотя бы даже:

    sock:=SOCKET(AF_INET, SOCK_STREAM, 0);
    CloseSocket(sock);



    То память начинает утекать, или так и должно быть?
    (сужу по Диспетчеру задач:), не знаю чем еще смотреть в Д6??).

    Заранее благодарен! Я только учусь, сильно не пинайте
    Есть еще парочка интересных вопросов, но только после этого)
  • 0xFFFF (15.10.12 19:22) [1]
    WSAStartup($101, WDATA);
    WSACleanup();
    в основном потоке(который создает work_thread потоки)
  • Rouse_ © (15.10.12 19:39) [2]
    Не факт что утекать, в диспетчере отображаются не те данные за которыми нужно следить, поставь FastMM, если что-то течет - он расскажет поподробней.
  • 0xFFFF (16.10.12 13:09) [3]
    WSAStartup($101, WDATA);
    WSACleanup();


    Надо делать в каждом потоке? или только при запуске и завершении основного потока приложения?

    По результатам тестов с FastMM'ом вроде бы выяснилось что утечки нет, но теперь следующая ситуация, если в рабочий цикл потока(вместо <работа> на листинге в выше) поместить:

    sock:=SOCKET(AF_INET, SOCK_STREAM, 0);
    FillChar(addr,SizeOf(TSockAddr),0);
    addr.sin_family:=AF_INET;
    addr.sin_port:=HtoNS(proxy_port);
    addr.sin_addr.S_addr:=inet_Addr(pchar(proxy_host));

    CloseSocket(sock);



    всё работает, а если одну строку изменить след. образом:

    addr.sin_addr.S_addr:=inet_Addr(pchar(host_to_ip(proxy_host)));



    т.е. используется функция:

    function host_to_ip(const host: string): string;
    var
     he: PHostEnt;
    begin
     he:= gethostbyname(PChar(host));
     if Assigned(he) then
       Result:= inet_ntoa(PInAddr(he.h_addr_list^)^)
     else
       Result:= PChar(host);
    end;



    то время от времени выпадает ошибка. В чем прикол?
  • brother © (16.10.12 13:15) [4]
    > то время от времени выпадает ошибка.

    Антон! Не партизань! Говори, что за ошибка!
  • 0xFFFF (16.10.12 13:29) [5]
    потоки порождаются таким образом:

        for i:=1 to nThread do
        begin
          CloseHandle(CreateThread(nil, 0, @work_thread, pointer(i), 0, h));
        end;



    Причем если CreateThread заменить на:

    CloseHandle(BeginThread(nil, 0, @work_thread, pointer(i), 0, h));



    То вроде бы ошибки пропадают, но появляется ошибка, при выполнении в основном потоке таких строк:
      EnterCriticalSection(CS);
      NOW_WORK:=FALSE;
      LeaveCriticalSection(CS);

    Кто разъяснит что и как, буду очень благодарен
  • brother © (16.10.12 16:28) [6]
    Ты точно партизан...
  • 0xFFFF (16.10.12 18:12) [7]
    Акцесс Виолэйшн) Есть какая-то общая методика отладки при мультипоточности? А то не могу поймать где (
  • 0xFFFF (16.10.12 18:14) [8]
    В качестве эксперимента убрал(не использую теперь(():


    function host_to_ip(const host: string): string;
    var
    he: PHostEnt;
    begin
    he:= gethostbyname(PChar(host));
    if Assigned(he) then
      Result:= inet_ntoa(PInAddr(he.h_addr_list^)^)
    else
      Result:= PChar(host);
    end;



    Прошло, но дальше при использовании recv, send в не блокирующем режиме опять ошибки ((
  • MetalFan © (16.10.12 19:58) [9]
    чот мне кажется ты со строками намудрил...  приведение (и запоминание) к PChar результата функций, возвращающих string, имхо, не самый хороший подход.
  • 0xFFFF (16.10.12 21:34) [10]
    Хорошо, упрощенный вариант ситуации, выдающий ошибку:


    function my_thread():dword; stdcall;
    var
    he: PHostEnt;
    ip: string;

    begin
    while NOW_WORK=TRUE do
    begin
     he:= gethostbyname('google.com');
     if Assigned(he) then ip:=inet_ntoa(PInAddr(he.h_addr_list^)^);
     sleep(1000);
    end;
    Result:=0;
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    var
    h: cardinal;
    i: integer;

    begin
    NOW_WORK:=TRUE;

    for i:=1 to 500 do CloseHandle(CreateThread(nil, 0, @my_thread, nil, 0, h));

    end;



    Рано или поздно акцесс виолэйшн. ПОЧЕМУ?
  • MetalFan © (16.10.12 22:40) [11]

    > function my_thread():dword; stdcall;

    И
    DWORD WINAPI ThreadProc(
     LPVOID lpParameter
    );


    список параметров несколько отличается. не знаю, возможно это в данном случае не критично, но все же.
    И для чего используется CreateThread? BeginThread более правильным будет использовать, имхо.

    А если сменить ip: string на ip: PChar, не "полегчает" ли?
  • Сергей М. © (16.10.12 23:31) [12]

    > акцесс виолэйшн


    сообщение об AV-исключении обычно сопровождается информацией об адресе инструкции, выполнение которой привело к исключению.

    access violation at address XXXXXXXX

    что говорит Search->Find Error.. для этого адреса ?
  • NoUser (17.10.12 00:52) [13]
    WSAStartup($101, WDATA);
    WSACleanup();


    ! > только при запуске и завершении основного потока приложения

    ух > for i:=1 to 500 do CreateThread ( -  sleep(1000)  -  )

    > Рано или поздно акцесс виолэйшн. ПОЧЕМУ?
    1.
    ! he.h_addr_list   ?= nil
    ! PInAddr(he.h_addr_list^)  ?= nil


    2.
    ! BeginThread
    + function work_thread(param: pointer): dword;
    ! не stdcall;  



    ФАКультатив.
    Note  The gethostbyname function has been deprecated by the introduction of the getaddrinfo function.
    +-+
    The application must never attempt to modify this structure or to free any of its components. Furthermore, only one copy of this structure is allocated per thread, so the application should copy any information it needs before issuing any other Windows Sockets function calls.


    PS.
    CloseHandle(BeginThread(nil, 0, @work_thread, pointer(i), 0, h));


    CloseHandle - А может надо будет из основного потока дождаться завершения этого ?
  • 0xFFFF (17.10.12 14:24) [14]
    Всем привет! Еще раз спасибо за ответы, но на самом деле они не очень возымели действие и никаких советов, что конкретно делать в данной ситуации я не получил. Но спасибо, что не оставили без внимания.

    Самый главный мой метод, метод научного тыка дал некоторые результаты.

    Всё работает (и сокеты и всё остальное) с BeginThread без передачи параметра, т.е.:


    function my_thread():dword; stdcall;
    CloseHandle(BeginThread(nil, 0, @my_thread, nil, 0, h));



    Работает как с stdcall; так так и без.

    На данном этапе возникли новые вопросы, а также всплыли старые так и не нашедшие ответа:

    1. ПОЧЕМУ занимаемое программой место в памяти во время работы всегда растёт (понемножку конечно но все же, данные диспетчера задач:) ), хотя по идее в рабочем цикле идет переброс данных из массива в массив(используются TStringList методами append и delete, и  несколько массивов record элементами)?

    2. ЧЕМ отличается вызов stdcall от не stdcall? Работает и так и так, ПОЧЕМУ?

    FastMM говорит, что всё чики-чики.
    опции FastMM:
    UseRuntimePackages, FullDebugMode, ClearLogFileOnStartup



    --

    >> ФАКультатив.
    >> Note  The gethostbyname function has been deprecated by the introduction of the getaddrinfo function.
    >> +-+
    >> The application must never attempt to modify this structure or to free any of its components. Furthermore, only one copy of this structure is allocated per thread, so the application should copy any information it needs before issuing any other Windows Sockets function calls.
    Да, НО дальше там написано как раз таки про отдельные потоки и что с ними происходит.

    >> CloseHandle(BeginThread(nil, 0, @work_thread, pointer(i), 0, h));
    >> CloseHandle - А может надо будет из основного потока дождаться завершения этого ?
    Не обязательно, если хэдл тебе более не нужен
  • Сергей М. © (17.10.12 16:13) [15]

    > 1. ПОЧЕМУ занимаемое программой место в памяти во время
    > работы всегда растёт


    Видимо потому что ты слишком уверен в бескосячности своего "рабочего цикла"


    > 2. ЧЕМ отличается вызов stdcall от не stdcall?


    stdcall - все параметры передаются через стек, за баланс стека отвечает вызываемая подпрограмма.

    под не-stdcall (т.е. соглашение не указано) компилятор задейстует pascal fastcall

    pascal fastcall - первые три (или менее) параметра передаются через регистры EAX, EDX, ECX, остальные - через стек, за баланс стека отвечает вызываемая подпрограмма.


    > Работает и так и так, ПОЧЕМУ?


    Потому что параметр ты не используешь.
    Как только попытаешься (при CreateThread без stdcall или при BeginThread с не-pascalfastcall), так сразу получешь граблями.
  • 0xFFFF (17.10.12 16:58) [16]
    Сергей М, большое спасибо за подробные ответы, благодарен!

    >> Видимо потому что ты слишком уверен в бескосячности своего "рабочего цикла"
    В рабочем цикле просто из одного TStringList'a раскидывается по 3-ём другим,
    сокеты открываются и закрываются. Вроде всё тип-топ, проверил на несколько
    раз. В чем может быть камень?
    FastMM ничего не показал, есть варианты в какую сторону смотреть? Всё
    остальное работает. И судя по результатам работает правильно. Беспокоит
    только этот факт. Потому что программа оперирует не хилыми размерами данных
    и при такой тенденции может забить всю память наверное.
  • Сергей М. © (17.10.12 18:09) [17]

    > при такой тенденции может забить всю память наверное


    Как забьет - сразу получишь EOutOfMemory.
    И это будет верный признак твоего косяка, даже если "Вроде всё тип-топ".
  • 0xFFFF (17.10.12 18:22) [18]
    >> Как забьет - сразу получишь EOutOfMemory.
    >> И это будет верный признак твоего косяка, даже если "Вроде всё тип-топ".
    Как можно определить почему это происходит? куда смотреть и какими инструментами? Или ждать EOutOfMemory? только при его появлении можно будет определить причину?

    Прошу прощения за глупые вопросы
  • MetalFan © (17.10.12 18:46) [19]

    > данные диспетчера задач

    ну я бы больше верил отчету FastMM, а не диспетчеру.
    Понаблюдай за процессом лучше с помощью ProcessExplorer...
    Имхо в нем больше информации можно извлечь.
  • Сергей М. © (17.10.12 20:52) [20]
    Для начала убери к лешему все доп.потоки кроме одного.
    На нем и исследуй "тенденцию".

    Детальные результаты (исх.код + зависимость прироста используемой памяти от времени) приводи сюда.
  • DVM © (17.10.12 23:43) [21]

    > FastMM

    Но надо помнить, что он не следит за ресурсами системы
  • 0xFFFF (18.10.12 00:02) [22]
    Большое спасибо за такое активное участие, постараюсь к выходным. Пока что не будет возможности.
 
Конференция "Сети" » CreateThread(), CriticalSection, SOCKET() [D6, D7, Win2k, WinXP]
Есть новые Нет новых   [119138   +27][b:0][p:0.002]