Конференция "WinAPI" » WaitNamedPipe нагружает процессор [D7, WinXP]
 
  • Madlinx (26.01.12 17:42) [0]
    WaitNamedPipe нагружает процессор до 99% на сервере под управлением Win Server 2003. Без использования этой функции все работает нормльно. Подскажите в чем сожет быть дело?
    В коде клиента фрагмент который закомментирован нагружает процессор, ниже этого фрагмента код без использования WaitNamedPipe работает без проблем.

    Код сервера:
    procedure TPipeThread.Execute;
    var
     hServiceInfoPipe, hThread: THandle;
     ServiceInfoPipeName: PWideChar;
     ServiceInfoPipeConnected: Boolean;
     dwThreadId, dwBytesRead: DWORD;
    begin
     ServiceInfoPipeName:=PWideChar('\\.\PIPE\MyPipe');
     ServiceInfoPipeConnected:=False;
     dwThreadId:=0;
     hServiceInfoPipe:=INVALID_HANDLE_VALUE;
     hThread:=0;
     while True do
     begin
       hServiceInfoPipe:=CreateNamedPipe(
         ServiceInfoPipeName,           // pipe name
         PIPE_ACCESS_DUPLEX,            // read/write access
         PIPE_TYPE_MESSAGE or           // message type pipe
         PIPE_READMODE_MESSAGE or       // message-read mode
         PIPE_WAIT,                     // blocking mode
         PIPE_UNLIMITED_INSTANCES,      // max. instances
         SizeOf(TServiceInfoPipeData),  // output buffer size
         SizeOf(TServiceInfoPipeData),  // input buffer size
         0,                             // client time-out (0 = 50 ms)
         nil);                          // default security attribute
       if hServiceInfoPipe = INVALID_HANDLE_VALUE then Continue;
       ServiceInfoPipeConnected:=ConnectNamedPipe(hServiceInfoPipe, nil);
       if GetLastError=ERROR_PIPE_CONNECTED then ServiceInfoPipeConnected:=True;
       if ServiceInfoPipeConnected then
       begin
         hThread := CreateThread(
           nil,                       // no security attribute
           0,                         // default stack size
           @ServiceInfoProcessing,    // thread proc
           LPVOID(hServiceInfoPipe),  // thread parameter
           0,                         // not suspended
           dwThreadId);               // returns thread ID
         if hThread=0 then
           begin
             CloseHandle(hServiceInfoPipe);
             Continue;
           end else CloseHandle(hThread);
       end else CloseHandle(hServiceInfoPipe);
     end;
    end;



    Код клиента:
    procedure SendInfoToPipe(const ServiceEvent: Byte;
     ResultInfoStr: string);
    var
     ServiceInfoPipeName: PWideChar;
     hServiceInfoPipe: THandle;
     SrvInfoPipeData: TServiceInfoPipeData;
     cbWritten, dwMode: DWORD;
     fSuccess: Boolean;
    begin
     ServiceInfoPipeName:=PWideChar('\\.\PIPE\MyPipe');
     fSuccess:=False;
     with SrvInfoPipeData do
     begin
       EventCode:=ServiceEvent;
       ServiceResultInfo:=ResultInfoStr;
       EventDate:=Date;
     end;
     
     {while True do
     begin
       hServiceInfoPipe := CreateFile(
         ServiceInfoPipeName,   // pipe name
         GENERIC_READ or        // read and write access
         GENERIC_WRITE,
         0,                     // no sharing
         nil,                   // default security attributes
         OPEN_EXISTING,         // opens existing pipe
         0,                     // default attributes
         0);                    // no template file
       if hServiceInfoPipe <> INVALID_HANDLE_VALUE then Break;
       if GetLastError <> ERROR_PIPE_BUSY then Exit;
       if not WaitNamedPipe(ServiceInfoPipeName, NMPWAIT_USE_DEFAULT_WAIT) then Exit;
     end;}


     hServiceInfoPipe := CreateFile(
       ServiceInfoPipeName,   // pipe name
       GENERIC_READ or        // read and write access
       GENERIC_WRITE,
       0,                     // no sharing
       nil,                   // default security attributes
       OPEN_EXISTING,         // opens existing pipe
       0,                     // default attributes
       0);                    // no template file
     if hServiceInfoPipe = INVALID_HANDLE_VALUE then Exit;

     dwMode:=PIPE_READMODE_MESSAGE;
     fSuccess:=SetNamedPipeHandleState(
       hServiceInfoPipe,  // pipe handle
       dwMode,            // new pipe mode
       nil,               // don't set maximum bytes
       nil);              // don'
    t set maximum time
     if not fSuccess then Exit;
     fSuccess:=WriteFile(hServiceInfoPipe, SrvInfoPipeData, SizeOf(TServiceInfoPipeData),
       cbWritten, nil);
     CloseHandle(hServiceInfoPipe);
    end;

  • Сергей М. © (26.01.12 17:51) [1]

    > нагружает процессор


    Все то время пока она ждет  ?
    Не верю.
  • Madlinx (26.01.12 18:42) [2]

    > Все то время пока она ждет  ?
    > Не верю.

    Но это так! Клиентом является служба, а сервером приложение. И все, на первый взгляд, работает как положено - сообщения отправляются и принимаются, но в диспечере задач процесс клиента постепенно нагружает процессор.
  • Madlinx (26.01.12 18:49) [3]
    Еще замечу, что на WinXPx86, Win7x64, WinServer2008 работает нормально,
    а на 2-х серверах WinServer2003 наблюдается вышеописанный глюк.
  • Eraser © (26.01.12 23:49) [4]
    > [0] Madlinx   (26.01.12 17:42)

    откуда такой вывод? чем заняты другие потоки?
  • Сергей М. © (27.01.12 09:41) [5]
    Последовательность NamedPipeAPI-вызовов  на клиентской стороне у тебя вообще с ног на голову поставлена.

    Должно быть WaitNamedPipe -> CreateFile, а у тебя наоборот CreateFile -> WaitNamedPipe.

    Цитата из справки к WaitNamedPipe :


    If the function succeeds, the process should use the CreateFile function to open a handle to the named pipe. A return value of TRUE indicates that there is at least one instance of the pipe available
  • Madlinx (27.01.12 10:00) [6]

    > Должно быть WaitNamedPipe -> CreateFile

    Не обязательно. Вообще это мой первый опыт работы с каналами и делал я все по примеру на сайте MSDN:
    Многопоточный сервер: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365588(v=vs.85).aspx
    И клиент к нему: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365592(v=vs.85).aspx
    У меня у клиента реализовано так же, а именно:
    1. Пытаюсь подключиться к каналу.
    2. Если подключился, то выхожу из цикла и произвожу запись.
    3. Если не подключился, то, если последняя ошибка не ERROR_PIPE_BUSY (ОШИБКА_КАНАЛ_ЗАНЯТ) то выходим из процедуры.
    4. Если последняя ошибка ERROR_PIPE_BUSY (ОШИБКА_КАНАЛ_ЗАНЯТ) то ожидаем особождения канала и, если дождались, опять входим в цикл и возвращаемся к пункту 1 для повтора подключения к каналу.
  • Madlinx (27.01.12 10:09) [7]

    > чем заняты другие потоки?

    В потоках созданных сервером происходит следующее (пример чисто схематический)
    case InfoCode of
     1: ShowBaloonHint; //Всплывающее сообщение с текстом "1"
     2: ShowBaloonHint; //Всплывающее сообщение с текстом "2"
     3: ShowBaloonHint; //Всплывающее сообщение с текстом "3"
    end;
  • Сергей М. © (27.01.12 10:40) [8]

    > Madlinx   (27.01.12 10:00) [6]
    > Не обязательно


    Что значит "не обязательно" ?
    Накой шиш тогда вообще вызывать WaitNamedPipe, если следом за ним не вызывать CreateFile ?
    Ведь WaitNamedPipe по сути нужна для обнаружения клиентом в  интересующем его промежутке времени факта создания сервером экз-ра именованого канала.
    А зачем, спрашивается, его вообще обнаруживать, если не для последующего подключения к нему  и использования ?

    Иными словами, вызов WaitNamedPipe не обязателен к использованию, но если он клиентом таки был сделан, то именно для того чтобы минимизировать вероятность отказа последующего вызова CreateFile.

    К тому же, если уж сервером при CreateNamedPipe в параметре nDefaultTimeOut указан 0, то вызов клиентом WaitNamedPipe c параметром nTimeOut  = NMPWAIT_USE_DEFAULT_WAIT лишен смысла - ф-ция попросту немедленно вернет управление с результатом FALSE, если на момент вызова свободный и готовый для подключения экз-р канала не существует.


    > В потоках созданных сервером


    Сервер-то причем ?
    Коль скоро речь идет о WaitNamedPipe, то идет она именно о процессе клиента, а не сервера. Сервер НЕ использует WaitNamedPipe.
  • Madlinx (27.01.12 11:03) [9]

    > Накой шиш тогда вообще вызывать WaitNamedPipe, если следом
    > за ним не вызывать CreateFile ?


    Так CreateFile у меня и вызывается после WaitNamedPipe, когда цикл идет на второй круг после неудачной попытки подключения "с лету" (при входе в цикл) к занятому каналу.

    > К тому же, если уж сервером при CreateNamedPipe в параметре
    > nDefaultTimeOut указан 0, то вызов клиентом WaitNamedPipe
    > c параметром nTimeOut  = NMPWAIT_USE_DEFAULT_WAIT лишен
    > смысла

    Параметр 0 в CreateNamedPipe означает, что будет использовано значение по умолчанию, а это 50 мс. Выдержка из справки MSDN: A value of zero will result in a default time-out of 50 milliseconds.
  • Сергей М. © (27.01.12 11:12) [10]

    > делал я все по примеру на сайте MSDN..клиент к нему


    Ну так там же, в примере, последовательность вызовов не менее очевидна ..

    while True do begin
     if CreateFile(..)=ВалидныйХедндлКанала then
       Break; // выход из цикла - первая же (безо всякого ожидания) или последующая (после ожидания) попытка подключения прошла успешно
    ..
    // не удалось подключиться, подождем пока канал будет создан или сервер будет свободен
    if not WaitNamedPipe(..) then Exit; // канал по прежнему не существует или сервер занят - подключаться к нему бессмысленно, поэтому выходим из подпрограммы, хотя по-хорошему следовало бы уточнить причину отказа анализом GetLastError
    .. иначе идем в начало тела цикла, где пытаемся повторно подключиться к каналу
    end;
    .. здесь можно пользовать канал
  • Сергей М. © (27.01.12 11:21) [11]

    > CreateFile у меня и вызывается после WaitNamedPipe, когда
    > цикл идет на второй круг


    А, ну да .. while-цикл у тебя в закомментаренном коде я проглядел ..
    Тогда с последовательностью вызовов у тебя порядок.

    А вот с дифолтным таймаутом - беда.
    Поскольку WaitNamedPipe у тебя вообще ничего не ждет, ее вызывающий поток все то время, пока крутится while-цикл, находится в user-time, не переходя в kernel-time.
    Вот потому-то ты и видишь повышенную "нагрузку".
    И в этой ситуации странным выглядит поведение не Win Server 2003, а как раз иных ОС, где нет сколь-либо ощитумого роста нагрузки.
  • Madlinx (27.01.12 11:27) [12]

    > А вот с дифолтным таймаутом - беда.

    Т.е. нужно на сервере и на клиенте в таймаут выставить конкретные значения,
    например 1000?
  • Сергей М. © (27.01.12 11:34) [13]
    На клиенте нельзя указать конкретное значение таймаута ожидания.

    Можно лишь указать NMPWAIT_WAIT_FOREVER (ждать до потери пульса)  либо NMPWAIT_USE_DEFAULT_WAIT (ждать столько сколько сервер при CreateNamedPipe указал в nDefaultTimeOut)
  • Madlinx (27.01.12 11:49) [14]
    Все понял, попробую и отпишусь. Но это только завтра, когда буду на работе.
    Спасибо.
  • Madlinx (28.01.12 13:11) [15]
    Не помогло. К сожалению проблема осталась. Решил оставить без WaitNamedPipe
  • Сергей М. © (30.01.12 09:29) [16]
    Т.е. ты у тверждаешь что при NMPWAIT_WAIT_FOREVER бесконечно ждущая при этом WaitNamedPipe в однопоточном клиентском процессе приводит к картине с ощутимым ростом нагрузки на CPU для данного потока этого процесса, так ?
  • Madlinx (30.01.12 12:19) [17]
    Значит так, обнаружилось еще кое что...
    Клиентское приложение у меня в 2 независимых потока обрабатывает файлы (открепляет и прикрепляет файлы к/от сообщений Novel GroupWise). Было замечено, что проблема возникает только когда работает поток открепления файлов и даже в том случае, если запись в канал из этого потока не производится. Т.е. если работает этот поток и используется WaitNamedPipe , то проблемя существует, а если WaitNamedPipe не используется, то проблемы нет (замечу что даже в том случае, когда из этого потока в канал не производится). У меня есть подозрения, что в этом как-то замешан цикл
    while ... do ... используемый в потоке. В этом цикле обрабатывается строка, например, TestString
    while Pos(',',TestString)>0 do TestString[Pos(',',TestString)]:=#13;


    Почему я подозреваю этот цикл? Потому что в обоих потоках код, в принципе, аналогичен, за исключением вышеописанного цикла, который во втором потоке используется в 4 местах.
  • Сергей М. © (30.01.12 12:37) [18]
    Значит так: никакие потоки кроме того в котором осуществляется вызов WaitNamedPipe никого сейчас не интересуют и должны быть исключены из рассмотрения, ибо вопрос быз задан конкретно о WaitNamedPipe.

    Посему без ответа на вопрос в [16] дальнейшие рассуждения на эту тему бессмысленны.
  • Сергей М. © (30.01.12 12:46) [19]
    Все ведь просто выясняется:

    Делаешь два простейших тестовых проекта - сервера и клиента.
    Можно даже сделать их консольными.

    В серверном приложении в одном единственном потоке вызываешь CreateNamedPipe и следом же усыпляешь этот поток по Sleep(INFINITE)

    В клиентском приложении в одном единственном потоке вызываешь единственную ф-цию WaitNamedPipe с NMPWAIT_WAIT_FOREVER

    Стартуешь первым сервер, затем клиент.

    Идешь в диспетчер задач и смотришь на CPU-нагрузку клиента.

    Проделываешь все это на всех интересующих ОС, результаты в табличном виде приводишь сюда.
 
Конференция "WinAPI" » WaitNamedPipe нагружает процессор [D7, WinXP]
Есть новые Нет новых   [134430   +4][b:0][p:0.003]