Aplicación se congela al hacer Pings masivo
#21
Escrito 16 marzo 2009 - 11:00
Saludos.
#22
Escrito 16 marzo 2009 - 11:02
la lentitud o el error de tipos?
#23
Escrito 16 marzo 2009 - 11:06
#24
Escrito 16 marzo 2009 - 11:24
De esta forma el SO le da toda la prioridad a la ejecucion del hilo.
constructor TThreadPing.Create(IP: String); begin cIP := IP; //guarda la IP en una variable privada inherited Create(True); Priority := tpTimeCritical; resume; end;
o de esta forma, no se interrumpe el proceso y el hilo se ejecuta cuando hayan recursos disponibles...
constructor TThreadPing.Create(IP: String); begin cIP := IP; //guarda la IP en una variable privada inherited Create(True); Priority := tpIdle; resume; end;
Puedes jugar con los valores de prioridad.
tpIdle
El hilo sólo ejecuta cuando el sistema esta parado. Windows no interrumpirá a otros hilos para ejecutar un hilo con prioridad tpIdle.
tpLowest
La prioridad del hilo está dos puntos por debajo de la normal.
tpLower
La prioridad del hilo está un punto por debajo de la normal.
tpNormal
El hilo tiene una prioridad normal.
tpHigher
La prioridad del hilo está un punto por encima de la normal.
tpHighest
La prioridad del hilo está dos puntos por encima de la normal.
tpTimeCritical
El hilo tiene la prioridad más alta.
Si quieres leer mas, Aqui
#25
Escrito 16 marzo 2009 - 11:59
¿Porqué el hilo no tiene el atributo o campo para que el mismo sepa hacia donde hacer ping?
Según como lo entiendo, el constructor Create() que definiste cambia el valor de cIP por el pasado por el parámetro. Luego, en el método SetReloj se lee esta variable cIP.
No si es que funciona... pero a mi no me cuadra. Si creo un hilo y paso la IP 1 (es un ejemplo) luego un segundo con una IP 2, y así sucesivamente llegarás a que en cIP tengas el valor del último hilo. Ahora bien, el método SetReloj va leyendo cIP y por ello es que algo me late a que siempre leerá el último IP, y no así los primeros.
En definitiva... creo que todos los hilos terminan haciendo ping a la misma IP, la última.
Mi consejo: añade un atributo IP y haz que "Recibe.Host" reciba el valor desde éste.
Por otro lado me extraña que no tengas variables del tipo TThreadPing para manejar los hilos... directamente estás creando al vuelo.... ¿es legal estar haciendo TThreadPing.Create()?
No usé hilos nunca pero yo me esperaba algo como:
Hilo1 := TThreadPing.Create().
El tema del Timer no me cierra. Si ya tenés los hilos ¿para que el timer?
Yo más bien buscaría que los propios hilos implementen de forma internan un timming y vuelvan a hacer ping.
Desconozco si es posible hacer ese timming dentro del hilo, pero por allí también va la cosa.
Saludos,
#26
Escrito 16 marzo 2009 - 12:40
la duda que tienes de la variable es lo mismo que hacerlo con atributos, el asunto es que siempre se crea una nueva instancia de la clase, y cada una se esta ejecutando por separado, de esta forma la variable nunca se sobreescribiria el valor, cosa que si sucederia si la sacamos del objeto y la declaramos publica en la unit.
Con respecto de crearlos sin asignarlo a una variable no tiene ningun problema, no veo cual seria la diferencia.
NOTA: Estoy hablando en base a experiencia personal, no tengo ninguna documentacion al respecto si alguien puede ilustrarnos al respecto estare muy agradecido, mi formacion con los hilos es auto didacta.
#27
Escrito 16 marzo 2009 - 01:24
Disculpa si pareciera que llevo la contraria... pero no comprendo. cIP no es privada a la clase, es local (puede accederse a ella desde toda la unit):a ver Delphius, trato de llevarte la secuencia:
la duda que tienes de la variable es lo mismo que hacerlo con atributos, el asunto es que siempre se crea una nueva instancia de la clase, y cada una se esta ejecutando por separado, de esta forma la variable nunca se sobreescribiria el valor, cosa que si sucederia si la sacamos del objeto y la declaramos publica en la unit.
var FMain: TFMain; Conteo: Integer; cIP: String; implementation
Y en la definición de la clase TTHreadPing no está:
TThreadPing = class(TThread) private procedure SetReloj; protected procedure Execute; override; public Host: String; S: TLabel; Recibe: TIdIcmpClient; G: TJvGIFAnimator; constructor Create(IP: String; Destino: TLabel; Imagen: TJvGIFAnimator); end;
Lo que noto es que cada vez que se realiza un Create, se está cambiando el valor de cIP:
constructor TThreadPing.Create(IP: String; Destino: TLabel; Imagen: TJvGIFAnimator); begin cIP := IP; //guarda la IP en una variable privada S := Destino; G := Imagen; inherited Create(False); end;
Por lógica, tras crear 24 hilos, en cIP nos quedamos con el último valor.
Puede que como dices que no haya problemas. En ocasiones no es necesario tener referencias a los objetos que creamos. Tal vez este sea un caso. Nomás me resulta extraño puesto que por lo que he estado leyendo en la ayuda, y en algunos ejemplos, sobre la clase TTHread, veo que sigue la misma regla que para cualquier objeto:Con respecto de crearlos sin asignarlo a una variable no tiene ningun problema, no veo cual seria la diferencia.
NOTA: Estoy hablando en base a experiencia personal, no tengo ninguna documentacion al respecto si alguien puede ilustrarnos al respecto estare muy agradecido, mi formacion con los hilos es auto didacta.
MiHilo := TMiThread.Create(...)
Lo que no me queda claro es que sucede (por defecto) cuando finaliza... ¿se libera?
Yo no los he puesto en práctica... sólo comento algunas observaciones que me resultaron raras amigo.
Yo me imagino tener 24 hilos creados y mantenerlos (aunque en la ayuda dicen que no es aconsejable tener más de 16). Mandarlos a suspender por tiempo y volverlos a ejecutar. No me parece demasiado viable crear y destruir cada dos pos tres.
Veo que la clase TThread cuenta con los métodos Suspend y Resume... y me parece que están para ello.
¿Porqué no algo como esto?
Hilo1.Suspend; Sleep(200); Hilo2.Resume;
O mejor aún, disponer de un array de hilos y poder trabajar con ellos con un índice. Estuve viendo que existe incluso la clase TThreadList.
No se amigo, reconozco que hablo desde la ignorancia pero dejame comentarte que me siento intrigado.
Saludos,
#28
Escrito 16 marzo 2009 - 01:45
Los pines toman su "tiempo" por más milisegundos que demoren. Yo me pregunto si será "cómodo" hacer algo como:
for i := 0 to 23 do PingTo(ListIP[i]);
Habría que hacer la prueba.
Saludos,
#29
Escrito 16 marzo 2009 - 01:54
si, no me habia fijado donde fue a parar el cIp, en mi idea era una variable interna del TThread que recibe el valor por un parametro del create, ahi llevas la razon...
Con respecto a la destruccion no lo habia pensado, y me gusta lo que propones, un solo hilo que haga todos los pines.
#30
Escrito 16 marzo 2009 - 02:08
No es llevar la contraria, es ue cuando se tiene razon hay que decirlo...
si, no me habia fijado donde fue a parar el cIp, en mi idea era una variable interna del TThread que recibe el valor por un parametro del create, ahi llevas la razon...
Con respecto a la destruccion no lo habia pensado, y me gusta lo que propones, un solo hilo que haga todos los pines.
Tampoco es para que me dieras el crédito amigo. No creo tener razón, simplemente puse el ojo y NewDelphius se metió a escribir
En realidad tanto la idea de hilos como de que uno sólo atienda todo se la merece Domingo, el fue quien comentó el tema, nomás yo vuelvo a poner sobre la mesa de sano debate.
Ahorita me voy a merendar, cuando vuelva voy a ver si logro preparar un ejemplo... si alguien ya lo tiene ¡mejor!
Saludos,
#31
Escrito 16 marzo 2009 - 02:17
// . . . procedure TForm1.FormCreate(Sender: TObject); var Index, IP: Integer; begin IP := inet_addr('143.1.14.200'); if CloseHandle(IcmpCreateFile) then for Index := Low(Reply) to High(Reply) do begin Reply[Index].Address.S_addr := IP; CreateThread(nil, 0, @Thread, Ptr(Index), 0, PDWORD(0)^); Inc(PChar(@IP)[3]); end; end; procedure TForm1.PingStatus(var Message: TMessage); const Colors: array [Boolean] of TColor = (clRed, clBlack); var x, y: Cardinal; Str: string; begin x := (Message.WParam mod 3) * 300; y := (Message.WParam div 3) * 60; with Reply[Message.WParam] do begin Canvas.Font.Color := clBlack; Canvas.TextOut(x + 50, y, inet_ntoa(Address)); case Status of 11000: Str := 'IP_SUCCESS'; 11001: Str := 'IP_BUF_TOO_SMALL'; 11002: Str := 'IP_DEST_NET_UNREACHABLE'; // . . .
Salud!
Archivos adjuntos
#32
Escrito 16 marzo 2009 - 02:36
...Yo me imagino tener 24 hilos creados y mantenerlos (aunque en la ayuda dicen que no es aconsejable tener más de 16). Mandarlos a suspender por tiempo y volverlos a ejecutar. No me parece demasiado viable crear y destruir cada dos pos tres...
En este caso no se debe usar la suspensión porque consume ciclos de procesador al llamar a las APIs necesarias las cuales "entran" hasta el nucleo. Crear y Destruir (el objeto) es tambien una mala idea
Y ahora ando pensando si es bueno tener 24 hilos.... ¿porqué no uno como comentó Domingo? La pregunta que me hago es si es muy adecuado que atienda a todos... ¿"aguantará"?...
Pues seoane no especifico la cantidad de hilos, pero si utilizó el plural. Si crearas un solo hilo funcionaria, pero debes considerar que si todas las PCs estan "desconectadas" excepto la ultima, el retardo se incrementaria en cada repeticion y habria una respuesta de tiempo de ese unico equipo despues de un intervalo prolongado.
...Finalmente considero que el rango razonable de hilos por proceso para este tipo de aplicaciones es de 256 a 1024...
Salud.
#33
Escrito 16 marzo 2009 - 05:04
Saludos.
PD. por lo menos aprendí lo básico sobre los Threads
#34
Escrito 17 marzo 2009 - 10:13
Hola Javier, no comprendo del todo el código (para variar )... me llama la atención como es que se enganchan las cosas...
...Yo me imagino tener 24 hilos creados y mantenerlos (aunque en la ayuda dicen que no es aconsejable tener más de 16). Mandarlos a suspender por tiempo y volverlos a ejecutar. No me parece demasiado viable crear y destruir cada dos pos tres...
En este caso no se debe usar la suspensión porque consume ciclos de procesador al llamar a las APIs necesarias las cuales "entran" hasta el nucleo. Crear y Destruir (el objeto) es tambien una mala idea
Y ahora ando pensando si es bueno tener 24 hilos.... ¿porqué no uno como comentó Domingo? La pregunta que me hago es si es muy adecuado que atienda a todos... ¿"aguantará"?...
Pues seoane no especifico la cantidad de hilos, pero si utilizó el plural. Si crearas un solo hilo funcionaria, pero debes considerar que si todas las PCs estan "desconectadas" excepto la ultima, el retardo se incrementaria en cada repeticion y habria una respuesta de tiempo de ese unico equipo despues de un intervalo prolongado.
...Finalmente considero que el rango razonable de hilos por proceso para este tipo de aplicaciones es de 256 a 1024...
Salud.
A ver si entiendo de manera superficial... Creas un hilo con la api CreateThread(), pasandole en el parámetro lpStartAddress la función Thead(). ¿En este parámetro se recibe el puntero a la rutina que debe ejecutar el Thread? ¿no?
La función Thread se encarga de crear una nueva ventana echo, luego encolas el mensaje WM_USER, pasandole como parámetro el índice del thread... hasta allí vamos...
pero en el parámetro adicional lParam mandas un ¿echo request?
Déjame ver si logro entender allí el tema... el API IcmpSendEcho recibe como los dos primeros parámetros la ventana echo creada previamente con IcmpCreateFile y la estructura record TInAddr con la IP a donde hacer ping.... en el parámetro sexto (ReplyBuffer) el ¿puntero hacia una posición del array record que tu definiste?
Allí ya me pierdo... si me puedes aclarar ese punto te lo agradecería.
Ya por último te pones a la escucha de los mensajes, que gracias al índice WParam te pones tan hermosamente a ubicar correctamente el texto referente a cada ping e IP. Aquí tengo cierta curiosidad que tiene que ver con el uso de los mensajes... ¿No es necesario identificar si el mensaje es WM_USER? A lo que voy, es que tengo pensado (favor de corregirme, no se si estoy en lo correcto) que el método PingStatus va "capturando" todos los mensajes y no simplemente WM_USER. No se... en otros ejemplos que he visto... por lo general veo un condicional o un case para comprobar si el mensaje recibido es el que nos interesa...
if Message.Msg = WM_USER then ....
Si tienes un poco de tiempo te agradecería que me uniera los conceptos, o si tienes alguna referencia o fuente de la que pueda leer.
EDITO:
Por cierto... ese número de hilos que indicas es grande comparado con el que leo en la ayuda:
Keeping track of too many threads consumes CPU time; the recommended limit is 16 active threads per process on single processor systems.
Saludos,
#35
Escrito 17 marzo 2009 - 02:38
...cuando hay echo no muestra "IP_SUCCESS", ¿es así expresamente?...
Lo que sucede es que he obviado que el valor de Status tras haber realizado un éco sería IP_SUCCESS, sin embargo estaba en mi inconciente que no sería asi, por lo que Message.LParam será 0 solo si ocurrio un error, en tal caso deberás revisar el valor de Status para saber qué ha ocurrido.
Nota; aunque va en contra de la documentación es posible que Status sea 0 y no 11000, habra que revisar :^)
Delphius; Te explico brevemente, cuando se crea el formulario (FormCreate) defino los IPs de todos los elementos del vector Reply, aparte de lo obvio lo hice de esta forma para poder inicializarlos de forma no secuencial (Ej. 192.168.1.2, 192.168.2.1, 192.168.1.30).
Al momento de inicializarlos tambien creo un hilo por cada elemento y al hilo le paso como parametro el indice del elemento en el vector al que habra de manejar cada hilo procesa individualmente un elemento y por ende un IP, el resultado del éco junto con el indice es enviado al formulario en forma de mensaje.
Aqui tu pregunta, en la linea 13 de Unit1.pas esta el valor del mensaje que ha de procesar ; message WM_USER;. Esta es la forma en que el Delphi maneja los mensajes, y como ya habras confirmado solo maneja el mensaje definido (para procesar cualquier mensaje podrías optar por un case en Application.OnMessage).
Salud
#36
Escrito 17 marzo 2009 - 02:45
Me parece un asunto muy interesante esto de los mensajes del API
Encontré esto :
Public Enum IP_STATUS IP_STATUS_BASE = 11000 IP_SUCCESS = 0 IP_BUF_TOO_SMALL = (11000 + 1) IP_DEST_NET_UNREACHABLE = (11000 + 2) IP_DEST_HOST_UNREACHABLE = (11000 + 3) IP_DEST_PROT_UNREACHABLE = (11000 + 4) IP_DEST_PORT_UNREACHABLE = (11000 + 5) IP_NO_RESOURCES = (11000 + 6) IP_BAD_OPTION = (11000 + 7) IP_HW_ERROR = (11000 + 8) IP_PACKET_TOO_BIG = (11000 + 9) IP_REQ_TIMED_OUT = (11000 + 10) IP_BAD_REQ = (11000 + 11) IP_BAD_ROUTE = (11000 + 12) IP_TTL_EXPIRED_TRANSIT = (11000 + 13) IP_TTL_EXPIRED_REASSEM = (11000 + 14) IP_PARAM_PROBLEM = (11000 + 15) IP_SOURCE_QUENCH = (11000 + 16) IP_OPTION_TOO_BIG = (11000 + 17) IP_BAD_DESTINATION = (11000 + 18) IP_ADDR_DELETED = (11000 + 19) IP_SPEC_MTU_CHANGE = (11000 + 20) IP_MTU_CHANGE = (11000 + 21) IP_UNLOAD = (11000 + 22) IP_ADDR_ADDED = (11000 + 23) IP_GENERAL_FAILURE = (11000 + 50) MAX_IP_STATUS = 11000 + 50 IP_PENDING = (11000 + 255) PING_TIMEOUT = 200 End Enum
Donde 11000 es la base y a partir de ahí va generando los valores, 0 es Satisfactoria, los demás tienen un incremento.
Muy bueno e interesante el manejo que haces Little Bro
Enlace
Salud OS
#37
Escrito 17 marzo 2009 - 03:00
...Encontré esto :
delphi
Public Enum IP_STATUS IP_STATUS_BASE = 11000 IP_SUCCESS = 0 IP_BUF_TOO_SMALL = (11000 + 1) // . . .
...
Esa no es documentacion oficial amigo, IP_SUCCESS esta definida como 11000, sin embargo lo mejor antes de procesar el valor de Status es procesar e valor retornado por la API IcmpSendEcho que ha de ser diferente de 0 al haber realizado satisfactoriamente el éco.
...Muy bueno e interesante el manejo que haces Little Bro ...
Gracias amigo
#38
Escrito 17 marzo 2009 - 03:18
Esa parte si la entendí, se que creas un hilo por cada IP y vas asociando a cada hilo con el índice del vector.
Delphius; Te explico brevemente, cuando se crea el formulario (FormCreate) defino los IPs de todos los elementos del vector Reply, aparte de lo obvio lo hice de esta forma para poder inicializarlos de forma no secuencial (Ej. 192.168.1.2, 192.168.2.1, 192.168.1.30).
Al momento de inicializarlos tambien creo un hilo por cada elemento y al hilo le paso como parametro el indice del elemento en el vector al que habra de manejar cada hilo procesa individualmente un elemento y por ende un IP, el resultado del éco junto con el indice es enviado al formulario en forma de mensaje.
Mi duda pasaba mas que nada por como se enganchaba CreateThread() con las demás funciones y métodos.
¡Gracias! No había visto ese pequeño detalleAqui tu pregunta, en la linea 13 de Unit1.pas esta el valor del mensaje que ha de procesar ; message WM_USER;. Esta es la forma en que el Delphi maneja los mensajes, y como ya habras confirmado solo maneja el mensaje definido (para procesar cualquier mensaje podrías optar por un case en Application.OnMessage).
Salud
procedure PingStatus(var Message: TMessage); message WM_USER;
Saludos,
#39
Escrito 18 marzo 2009 - 06:38
#40
Escrito 18 marzo 2009 - 09:33
Aun no entiendo como no tienes un jugoso proyecto entre manos...