|
AxIThread, librería ActiveX
AxIThread permite gestionar tantos procesos procesos como desee trabajando en segundo plano mientras su aplicación se dedica a otra tarea. Cada proceso se ejecutará en un hilo independiente bajo la supervisión de AxIThread, siendo su programa el encargado de administrar su funcionalidad.
Para ello la librería expone siete métodos que le otorgarán control sobre todas las acciones posibles (creación, eliminación, cambio de prioridad, etc) y dos eventos externos donde escribir el código a procesar y recuperar errores.
AxIThread puede ejecutar cualquier sección de código de su programa en un hilo independiente; basta con que indique la función que desea asignar como punto de entrada. Cada hilo puede tener dos puntos de entrada diferentes que, además, podrá modificar en cualquier momento.
Entre los distintos parámetros podrá establecer recursividad, y la función por defecto será llamada indefinidamente; asignar un intervalo, con lo que, periódicamente, se realizará una llamada al método predeterminado; habilitar protección al ejecutar código externo, con lo que se bloquearán nuevas llamadas mientras alguna esté en proceso, o deshabilitarla. Y en todo momento podrá solicitar otra tarea, que será ejecutada con preferencia sobre las demás.
Podrá crear los hilos con cualquier prioridad (dentro de los niveles permitidos) o cambiar ésta asignando un nivel inferior a tareas menos relevantes y prioridad superior a las más críticas.
Cambios en la versión 4.0
El componente ha sido resideñado por entero. Aunque se ha mantenido la sintaxis de los métodos en la medida de lo posible para facilitar la migración desde versiones anteriores, la funcionalidad y ámbito de los eventos ha variado completamente.
Ahora puede crear tantos hilos como desee. El número de hilos sólo está limitado por los recursos del sistema.
Nuevos manejadores de inicialización y terminación. Cada nuevo hilo, independientemente de los parámetros de creación, ejecutará la sección de inicialización antes de entrar en el modo ejecución, y la de finalización en cuanto salga del mismo. Ambas están garantizadas en cualquier circunstancia, siempre que haya implementado las mismas en su código.
Se han implementado tres eventos que se corresponden con las secciones de inicialización, ejecución y terminación. Puede prescindir de ellos deshabilitando la recepción de eventos (vea más adelante) y escribiendo sus propias funciones de manejo.
Ejecución del temporizador en espacio de memoria separado. En versiones anteriores esta funcionalidad residía en el hilo primario del programa. Si éste no podía atender nuevas peticiones (por estar suspendido, bloqueado o realizando tareas intensivas) las interrupciones del temporizador no se procesaban (acumulándose) hasta que el mismo quedara libre. Esto podía producir un comportamiento indeseado de los temporizadores.
Excepto los manipuladores que son generados de manera dinámica, todos los objetos y recursos son ahora automáticos (instanciados en el stack) y la liberación de los mismos está asegurada en cualquier situación por los mecanismos del lenguaje.
Definitivamente he separado las versiones developer y enterprise. Aunque a nivel funcional son idénticas, la versión enterprise requiere dos componentes para usarla en modo diseño, pero su rendimiento es muy superior en modo ejecución.
Edición enterprise
El componente servidor se ejecuta ahora como servicio en una máquina que corra wNT/2000. Pueden existir tantos servidores como se desee.
La configuración/verificación se realiza ahora por una nueva interfaz de usuario.
Cada servidor puede manejar ahora 4.294.967.295 clientes.
La edición enterprise de la versión 4.0 de AxIThread se compone de cuatro módulos que, a su vez, se clasifican en las categorías cliente y servidor:
Módulos servidor a. Servicio administrador de licencias b. Consola de administración
Módulos cliente
c. Cliente AxIthread d. Librería Active X
La parte servidor deberá instalarla en un sistema ejecutando NT/2000. La parte cliente correrá en cualquier plataforma windooze. Si bien basta con el componente Active X (d) para acceder a la funcionalidad de la librería en modo ejecución, necesitará los cuatro para utilizar la misma en modo diseño. Del mismo modo, cuando distribuya su aplicación, sólo necesitará incluir el componente Active X.
Usar AxIThread
Para hacer uso de este componente, deberá agregar la biblioteca de tipos de AxIThread a su proyecto mediante la opción Referencias del menú Proyecto (en VB). Seleccione la referencia 'AxIThread 4.0s type library' (versión developer) o 'AxIThread 4.0e type library' (versión enterprise).
Una vez haya cargado la misma, deberá crear una instancia al objeto 'Thread' de la librería,
Public miThreadObjeto as new Thread (declaramos el objeto) Public WithEvents miThreadObjeto as Thread ... ... Set miThreadObjeto = new Thread (creamos una instancia del mismo) ... ... Set miThreadObjeto = Nothing (la liberamos cuando ya no sea necesaria)
NOTA: La segunda declaración habilita la recepción de eventos; la primera no.
Hilos recursivos/no recursivos
Un hilo no recursivo es pasivo, esto es, permanece inactivo a la espera de mensajes que procesar. Cuando recibe un mensaje, realiza un ciclo completo para procesar el mismo y queda a la espera de nuevos mensajes.
Un hilo recursivo, por el contrario, es un hilo activo; procesa mensajes del mismo modo que un hilo no recursivo, pero en ausencia de ellos no permanece a la espera, sino que invoca constantemente el método predeterminado.
Suponga una función que compruebe la presencia de datos en un puerto de comunicaciones durante un intervalo concreto. Una vez finalizado el intervalo (o ante la recepción de datos), la función comunica la presencia o no de datos y devuelve los mismos o no.
Este es el tipo de función adecuada para ser invocada desde un hilo recursivo. éste invoca al método, y en función del resultado, comunica al proceso padre la presencia o no de datos. Así, indefinidamente, mientras no reciba un mensaje solicitando otra tarea. El proceso padre, queda liberado de realizar una comprobación periódica del puerto, debiendo atender únicamente las notificaciones que reciba desde el hilo secundario.
Los hilos recursivos, procesarán siempre primero los mensajes recibidos; sólo en ausencia de mensajes invocarán el método por defecto. De este modo, los requerimientos del proceso padre serán atendidos de manera preferente.
Los hilos no-recursivos, realizan un muy eficiente tiempo de espera, por lo cual consumen un casi nulo tiempo de procesador. Así, son indicados para tareas no periódicas, como podría ser leer los datos del puerto de comunicaciones del ejemplo anterior, cuya presencia conocemos gracias a la notificación del hilo recursivo.
Temporizadores
Puede establecer un determinado intervalo a la hora de crear un hilo, así, éste realizará una llamada periódica a un método, que no tiene por qué ser el predeterminado. En este caso se le asignará un temporizador que, de manera regular, según el intervalo establecido, indicará al hilo que ejecute la tarea solicitada.
Este comportamiento es independiente del modo de inicio del hilo (recursivo-no recursivo), siendo posible, además, establecer tareas distintas para uno u otro caso; el hilo, dependiendo del mensaje recibido, ejecutará una tarea u otra.
El temporizador puede habilitarse en el momento de crear el hilo o con posterioridad, y puede deshabilitarlo, modificar el intervalo, o la tarea a realizar en cualquier momento.
He de destacar que, en hilos recursivos, los mensajes del temporizador, o los directos, serán atendidos con preferencia al método predeterminado.
Ejecutar código en segundo plano; el punto de entrada
En un archivo ejecutable, no hay descripciones de variables y funciones. Toda referencia a cualquiera de ellas ha sido convertida al enlazar (link) a una posición de memoria relativa (desplazamiento) al punto de entrada del mismo. Cuando un programa se carga en memoria, lo hace en un punto concreto de la misma, y toda referencia a un dato apunta a la posición de memoria donde se almacena el mismo. Esta posición es el desplazamiento respecto a la posición origen del programa.
Cada vez que se crea un hilo, hay que asignarle su punto de entrada, esto es, la posición de memoria donde empezará a ejecutar código.
AxIThread, expone un evento (ThreadRun) donde podrá escribir el código que desee ejecutar en segundo plano. En VB, por ejemplo, aparecerá como
Private Function Thread1_ThreadRun(ByVal nThreadID As Long, ByVal nUserData As Long) As Long (donde nThreadID será la ID del hilo que hace la llamada)
Este será por defecto el punto de entrada de cada nuevo hilo que cree, pero podrá asignar un punto de entrada diferente al crear el hilo. La funcion que será el nuevo punto de entrada del hilo ha de seguir la descripción del evento, es decir, aceptará dos párametros por valor (que serán enteros largos), y devolverá un entero largo, p.e,
Private Function miThreadFunc(ByVal tid As Long, ByVal uData As Long) As Long Dim ValorDeRetorno as Long ... ... miThreadFunc = ValorDeRetorno End Function
Para establecer el nuevo punto de entrada, basta con que indique la posición de memoria de la función al llamar al método CreateThread.
En C/C++, basta con indicar un identificador de función. En VB, podrá hacerlo mediante el operador AddressOf(...) que devuelve la dirección de memoria del dato solicitado. Ahora bien, el uso del operador AddressOf, supone las siguientes restricciones
1- Sólo puede usarlo en un módulo (fichero .bas) 2- no podrá usarlo directamente, sólo puede invocarlo como argumento al llamar una función, por lo que necesitará una función que empaquete la dirección. 3- El dato argumento de AddressOf deberá estar en el mismo módulo, donde utilice el operador.
Por ejemplo, Private Function EmpaquetarDireccion(FuncAdd As Long) As Long EmpaquetarDireccion = FuncAdd End Function
Public Function ObtenerPuntoEntrada(cual as integer) as long Select case cual Case 1 ObtenerPuntoEntrada = EmpaquetarDireccion AddressOf(ThreadFuncA) Case 2 ObtenerPuntoEntrada = EmpaquetarDireccion AddressOf(ThreadFuncB) ... ... Case else ObtenerPuntoEntrada = 0 End select End Sub
Public Function ThreadFuncA(ByVal tid As Long, ByVal uData as Long) As Long
'codigo a ejecutar
ThreadFuncA = 0 End Function
Public Function ThreadFuncB(ByVal tid As Long, ByVal uData as Long) As Long
'codigo a ejecutar
ThreadFuncB = 0 End Function
El código anterior debe estar en el mismo modulo 'bas' donde utilice AddressOf, pero eso no le impide llamar desde las funciones a cualquier otra sección del programa
Public Function ThreadFuncX(ByVal tid As Long, ByVal uData As Long) As Long Dim longVar as Long longVar = FormW.FuncionV(ByVal tid, ByVal uData) ThreadFuncX = ModuloY.FuncionZ(ByVal tid, longVar, ...) End Function
Si ha establecido un temporizador puede asignarle al mismo un punto de entrada independiente, con lo que el hilo ejecutará una acción diferente cuando reciba un requerimiento del mismo.
Sin menoscabo de lo anterior, en cualquier momento podrá modificar los puntos de entrada predeterminados. Asímismo, ocasionalmente, podrá enviar un mensaje solicitando otra tarea (otra función) que será ejecutada de inmediato y una única vez volviendo después al comportamiento predeterminado. Así, cada hilo, puede tener asignadas hasta tres tareas distintas en un momento dado.
Todo ello le otorga una gran flexibilidad a la hora de ejecutar código en segundo plano.
Así, miObjetoThread.CreateThread (No se indica punto de entrada, se usará el predeterminado, evento suObjeto_ThreadFunc(...))
miObjetoThread.CreateThread(1,,-1, ObtenerPuntoEntrada(2)) miObjetoThread.CreateThread(1,,-1, AddressOf(ThreadFuncB)) (sólo en en mismo módulo) (el punto de entrada sera ThreadFuncB)
Dispone además de otros dos eventos donde puede implementar las rutinas de inicialización y terminación de cada nuevo hilo. Éstas serán llamadas inmediatamente antes y después de entrar en (y salir de) la sección de ejecución.
Private Function Thread1_ThreadInit(ByVal nThreadID As Long, ByVal nUserData As Long) As Long
Private Sub Thread1_ThreadTerm(ByVal nThreadID As Long, ByVal nUserData As Long, ByVal nReason as Long)
También puede escribir sus propios manejadores de inicialización y terminación; de nuevo, ajuste sus descripciones a los prototipos de los eventos, como se describe más adelante.
Algunas consideraciones sobre los eventos
Como ya he mencionado, AxIThread facilita tres eventos donde puede escribir el código a ejecutar en segundo plano:
Private Function objeto_ThreadInit(ByVal nThreadID As Long, ByVal nUserData As Long) As Long
Private Function objeto_ThreadRun(ByVal nThreadID As Long, ByVal nUserData As Long) As Long
Private Sub objeto_ThreadTerm(ByVal nThreadID As Long, ByVal nUserData As Long, ByVal nReason as Long)
El evento ThreadInit se ejecutará antes de entrar en la sección de ejecución, permitiendo realizar tareas de inicialización antes de pasar el control a la misma. Este evento recibe dos parámetros indicando la ID del hilo que hace la llamada y un parámetro definido por el usuario (vea CreateThread). Utilize el valor de retorno de esta función para indicar si el hilo debe o no ser inicializado. Cualquier valor distinto de cero será procesado como un error, la ejecución saltará directamente a la rutina de terminación y se eliminará el hilo. Si omite el valor de retorno, éste será por defecto nulo, o lo que es lo mismo cero, asumiendo que la inicialización ha sido correcta.
ThreadRun será el corazón de todo. Escriba aquí el código a procesar.
El evento ThreadTerm se procesará inmediatamente después de salir del bloque de ejecución, permitiendo así realizar las tareas de limpieza necesarias. Recibe un tercer parámetro indicando la causa de finalización.
Puede hacer uso de los eventos por defecto, o escribir sus propias rutinas de manejo, o usar ambas al mismo tiempo cambiando dinámicamente la sección de código a ejecutar.
Así, por ejemplo: Private Function init(ByVal tid as long, ByVal hwnd As Long) As Long init = 1 ' asumimos error
If algunaFuncion(ByVal hwnd) = 0 Then Exit Function
' inicializamos datos
init = 0 ' correcto
End Function
Private Function run(ByVal tid As Long, ByVal hwnd as Long) As Long 'codigo a ejecutar
run = IIf( zFunction(ByVal uData) <> 0,-1,0) ' establecer valor de retorno
End Function
Private Sub term(ByVal tid As Long, ByVal hwnd as Long, ByVal nReason as long) If nReason = &H7FFFFFFA Then Exit Sub ' no se completó la inicialización
For i = 0 To Forms.Count - 1 If Forms(i).hwnd = hwnd Then unload Forms(i) Exit Sub End If Next i
End Sub
Al crear el hilo, indique las rutinas que manejarán las distintas secciones, o nada si desea procesar esas tareas en los eventos o no procesarlas:
Public Function NuevoHilo() As Long Dim x As Form Set x = new Form
NuevoHilo = CreateThread( , , -1, ByVal x.hwnd, , , AddressOf run, , AddressOf init, AddressOf term) ' establecemos manejadores, parámetro de usuario, y valor de retorno
NuevoHilo = CreateThread( , , -1, ByVal x.hwnd, , , AddressOf run) ' no se procesará inicialización ni terminación, o se realizarán en los eventos por defecto
End Function
Evidentemente, el valor indicado como punto de entrada puede contener una dirección incorrecta. Así, cuando un hilo genera una excepción la acción a tomar sigue la lógica siguiente
- si el hilo es temporizado y/o recursivo y la excepción la ha causado el punto de entrada asignado por el usuario:
- en el primer caso se elimina el temporizador - en el segundo se deshabilita la recursividad En ambos, el hilo permanece activo; si ha definido una rutina de manejo de errores recibirá un mensaje de error en la misma. (vea tabla de códigos de error). - si el hilo es temporizado y/o recursivo y la excepción la ha causado el punto de entrada predeterminado:
- si ha habilitado la recepción de eventos, igual que la anterior. - si no ha habilitado la misma, el hilo es eliminado puesto que no tiene asignado un punto de entrada. Recibirá el código de error en la rutina o evento de terminación.
No existe un evento predeterminado para el manejo de los errores. Si desea procesar los mensajes de error que puedan producirse debe definir una subrutina de manejo siguiendo el prototipo siguiente: Private Sub errorHandler(ByVal tid As Long, ByVal uData As Long, ByVal nErrorCode as Long)
donde,tid, indica la ID del hilo que ha causado el error. uData, el parámetro de usuario. nErrorCode, código de error.
Códigos de error recibidos en rutina de error:
-. 2147483632 (hex 0x7FFFFFF0), no recursivo (función por defecto) -. 2147483634 (hex 0x7FFFFFF2), no recursivo (parámetro de mensaje) -. 2147483635 (hex 0x7FFFFFF3), programado no recursivo -. 2147483636 (hex 0x7FFFFFF4), recursivo (función por defecto) -. 2147483638 (hex 0x7FFFFFF6), recursivo (parámetro de mensaje) -. 2147483639 (hex 0x7FFFFFF7), programado y recursivo
En los casos 3 y 6, se habrá eliminado el temporizador. En los casos 4 y 5, el estado del hilo habrá cambiado a no recursivo. Códigos de error recibidos en rutina de terminación: -. 2147483642 (hex 0x7FFFFFFA), inicialización no completada -. 2147483644 (hex 0x7FFFFFFC), ningún error; procesamiento normal. -. 2147483647 (hex 0x7FFFFFFF), no hay definido punto de entrada.
NOTA: Cuando el componente se descarga de memoria (porque se cierre la aplicación o se elimine la referencia al mismo) múltiples hilos pueden ser finalizados al mismo tiempo, llamando a la rutina de salida que tengan definida. Evite realizar tareas que puedan demorarse en los manejadores de salida.
Modo protegido
La ejecución de código externo pueden estar (o no) protegida: en el primer caso, las invocaciones a función se realizarán una a una, impidiendo nuevas llamadas mientras una esté en proceso. Si las llamadas acceden a datos sensibles a ser manipulados por varios procesos al mismo tiempo (una variable global, por ejemplo), deberá habilitar la protección para evitar la corrupción de los mismos (vea más adelante). Si la protección está deshabilitada, las notificaciones se realizarán independientemente del número de llamadas en proceso. Si estas llamadas operan con valores no sensibles como variables locales, valores de retorno, arrays indexados o incluso variables globales (siempre y cuando sean de sólo-lectura), puede deshabilitar la protección. En el primer caso, obtiene más seguridad; en el segundo mayor rendimiento. La elección de un modelo u otro dependerá de la implementación del código de su programa. Si cada hilo tiene un punto de entrada diferente, o no opera con datos sensibles a ser manipulados por múltiples procesos al mismo tiempo, no será necesario que habilite protección y la ejecución del código será más eficiente.
La protección es un parámetro independiente para cada hilo; así, si múltiples hilos hacen uso de la misma sección de código, y ésta es sensible a la manipulación simultánea de datos, deberá habilitar protección para cada uno de ellos. La exclusión simultánea sólo es válida para hilos protegidos entre sí, y sólo se garantiza entre ellos, los protegidos nada saben de los desprotegidos y éstos, a su vez, nada saben de los primeros.
Suponga tres hilos que accedan a una función en cuyo cuerpo se incremente un contador para luego, según el valor del mismo, realizar una operación u otra. Si ha habilitado protección en dos de ellos, pero no en el tercero, los dos primeros no podrán acceder simultáneamente a la sección de código, pero sí el tercero, independientemente del estado de los primeros y, del mismo modo, si aquel para el que no ha habilitado protección está ejecutando la función, cualquiera de los primeros, podrá acceder a la misma.
Como la protección afecta al código
Si varios hilos acceden de manera simultánea a una función y no desea habilitar protección, no utilice variables globales en el cuerpo de las funciones de notificación, a menos que sean de solo lectura. Observe el siguiente fragmento de código:
Private Function FuncionPuntodeEntrada(ByVal nThreadID As Long, ByVal uData as Long) as longGlobalData = GlobalData + 1 If GlobalData = 5 Then... FuncionPuntodeEntrada = ValorDeRetorno ...End IfEnd Function
Cada vez que la función es llamada se incrementa el valor de la variable GlobalData; si su valor es 5, se realizan una serie de operaciones y se devuelve el valor de retorno asignado al hilo. Si un hilo llama a la función cuando la variable contiene el valor 3, incrementa esta variable y antes de chequear el contenido, el sistema pasa el control a otro hilo que a su vez modifica la misma, cuando el control retorne al primer hilo, el contenido de la variable no será el esperado 4 sino 5 (y se ejecutará el bloque if).
Este es el tipo de situación que debe tratar de evitar, bien empleando variables locales, con lo cual cada hilo trabajará con una copia distinta, bien usando arrays,
Private Function FuncionPuntodeEntrada(...)GlobalData(nThreadID) = GlobalData(nThreadID) + 1 If GlobalData(nThreadID) = 5 Then... FuncionPuntodeEntrada = ValorDeRetorno(nThreadID) ...End IfEnd Function
Si le es absolutamente indispensable chequear una variable global, asegúrese que es de solo lectura o implemente algún tipo de sincronización. El caso más sencillo sería:
Private Function FuncionPuntodeEntrada(...)Static Lock as Boolean
Do while LockSleep(1) 'pasemos el control a otro hiloLoop
Lock = true GlobalData = GlobalData + 1 If GlobalData = 5 Then... FuncionPuntodeEntrada = ValorDeRetorno ...End If Lock = falseEnd Function
Si aún así, experimenta problemas, deberá habilitar protección.
El paso de argumentos
Los métodos expuestos por AxIThread requieren parámetros obligatorios, y/o opcionales. Entre los opcionales, cabe diferenciar aquellos que toman valores por defecto, y aquellos que no lo hacen. En todo caso, todo parámetro opcional puede obviarlo, pues en su ausencia tomará su valor por defecto o será descartado. Así, son válidas las llamadas siguientes:
nThrID = miThreadOb.CreateThread nThrID = miThreadOb.CreateThread(1,,500,,1,,,nResult) nThrID = miThreadOb.CreateThread(, 1, , , ,25000)
Puede prescindir de aquellos parámetros opcionales que no seán de interés, pero los indicados deben ir en la posición que les corresponde.
Métodos
En la descripción del formato de los métodos expuestos por AxIThread, los parámetros entre corchetes indican parámetros opcionales. Si, además, aparece un valor, este será el valor que tomará por defecto en caso de no ser indicado.
PauseThread
Suspende la ejecución del hilo indicado hasta que sea resumido o eliminado.
Este método no retorna ningún valor.
Formatos (C & VB) void PauseThread (long nThreadID)
PauseThread (nThreadID as long)
nThreadID, ID del hilo a suspender.
ResumeThread
Resume un hilo previamente suspendido.
No retorna ningún valor.
Formatos (C & VB) void ResumeThread (long nThreadID)
ResumeThread (nThreadID as long)
nThreadID, ID del hilo a resumir.
ThreadPriority
Establece la prioridad del hilo al valor indicado en nLevel.
No retorna ningún valor.
Formatos (C & VB) void ThreadPriority (long nThreadID, [long nLevel = 0])
ThreadPriority (nThreadID as long, [nLevel as long = 0])
nThreadID, ID del hilo.
nLevel, nuevo nivel de prioridad. Los valores admitidos son de 0 (mínima) a 4 (máxima), siendo 2 el valor por defecto del sistema (8).
CreateThread
Crea un nuevo hilo de ejecución, de acuerdo a los argumentos especificados; siempre ejecutará un ciclo completo nada más ser creado, excepto si el hilo es temporizado, en cuyo caso el primer ciclo no se ejecutará hasta que venza el intervalo asignado.
Si no hay error, o éste no ha impedido la creación del hilo, CreateThread devuelve un entero largo que será la ID asignada al hilo que lo identifica. Deberá indicar esta ID cuando llame a las demás funciones. En caso contrario retornará 0, y colocará un código de error en nResult.
Formatos (C & VB) long CreateThread([BOOL bProtected = true], [short nPriority = 2],[long nRetCode = 0], [long nUserParam = 0], [long nAutoCallInterval = 0], [BOOL bRecursive = false], [long RunHandler = 0], [long AutoHandler = 0], [long InitHandler = 0], [long TermHandler = 0], [long ErrHandler = 0], [long *nResult])
CreateThread([bProtected As Boolean = true], [nPriority As Integer= 2],[nRetCode As long = 0], [nUserParam As long = 0], [nAutoCallInterval As long = 0], [bRecursive As Boolean= false], [RunHandler As long = 0], [AutoHandler As long = 0], [InitHandler As long = 0], [TermHandler As long = 0], [ErrHandler As long = 0]) As long
bProtected, habilitar protección (verdadero), no habilitar (falso). Por defecto, estará habilitada, (vea nota).
nPriority, nivel de prioridad predeterminado. Los valores admitidos son de 0 (mínima) a 4 (máxima). Por defecto 2 (prioridad normal).
nRetCode, código de retorno de un hilo (valor que forzará la salida del mismo), (vea al pie).
nUserParam, parámetro de usuario (enviado a todas las rutinas).
AutoCallInterval, intervalo a asignar al temporizador, (vea nota).
bRecursive, si indica (verdadero) el hilo será iniciado como recursivo, (vea nota).
RunHandler, dirección de memoria que será el punto de entrada del hilo, (vea nota).
AutoHandler, punto de entrada para el temporizador.
InitHandler, punto de entrada para inicialización.
TermHandler, punto de entrada para terminación.
ErrHandler, punto de entrada para manejo de errores.
nResult, resultado de la operación.
Códigos de error devueltos en nResult
0, ningún error. 1, se ha creado el hilo pero no ha sido posible crear el temporizador. 8, no ha sido posible encontrar el punto de conexión de eventos. 15, (hex. F), no ha sido posible crear el hilo.
El código de error 8, aparecerá cuando instancie el objeto como una referencia sin habilitar eventos. Puede usar todas las funcionalidades de la librería, pero NO a través de los eventos predeterminados.
Los hilos permanecen activos hasta que se descargue el componente, se les indique terminar mediante ThreadStop, o porque tengan establecido un código de retorno. Si establece un código de retorno específico para un hilo, y la función que es llamada por el mismo devuelve ese valor, el hilo terminará y será eliminado. Este valor es, por defecto, nulo (0). Si la función llamada no devuelve nada (a efectos prácticos eso es lo mismo que 0), el hilo recibirá el valor de retorno asignado y terminará inmediatamente. Ténga en cuenta que, dado que cada hilo puede tener asignadas hasta tres tareas diferentes en un momento dado, pero un único valor de retorno; cualquiera de ellas puede forzar la eliminación del hilo.
StopThread
Detiene la ejecución del hilo indicado en nThreadID.
Devolverá 0 si no ha habido error ó 15 (hex 0xF) si no existe el hilo indicado en nThreadID.
Formatos (C & VB) long StopThread(long nThreadID, [BOOL bIgnoreMsgQueue = false])
StopThread(nThreadID As long , [bIgnoreMsgQueue As Boolean = false]) As long
nThreadID, ID del hilo a detener. Esta ID es la que devuelve CreateThread y que identifica a cada hilo.
bIgnoreMsgQueue, ignorar mensajes pendientes. Si está configurado como falso, se procesarán los mensajes pendientes antes de eliminar el hilo. Si está configurado como verdadero, se eliminará el hilo inmediatamente, ignorando los mismos.
LoopThread
Coloca un nuevo mensaje en la cola de mensajes del hilo referido como nThreadID, indicando opcionalmente la operación a realizar.
Devolverá 0 si no ha habido error ó 15 (hex 0xF) si no existe el hilo indicado en nThreadID.
Formatos (C & VB) long LoopThread(long nThreadID, [long FuncAdd = 0])
LoopThread(nThreadID As long, [FuncAdd As long = 0]) As long
nThreadID, ID del hilo al que comunicar mensajes.
FuncAdd, dirección de memoria donde realizar un ciclo completo.
Los hilos recursivos llaman indefinidamente el método predeterminado; los programados (temporizados) cada vez que vence el intervalo establecido; los recursivos y programados en ambos casos. Sin embargo, los hilos no recursivos ni programados, llaman a su punto de entrada predeterminado una sola vez nada más ser creados, y luego quedan a la espera de realizar un nuevo ciclo.
Así, esta función cubre dos propósitos:
En primer lugar, para indicar a un hilo recursivo y/o programado que realice otra tarea, llame esta función con la dirección de una función de su programa. Así, este hilo puede llegar a realizar hasta tres tareas diferentes en un momento dado.
En segundo lugar, para indicar a un hilo inactivo (no recursivo ni programado), que ejecute un nuevo ciclo (bien del método predeterminado, bien de la función indicada).
ModifyThread
Permite alternar entre modo recursivo/no recursivo; establecer, modificar o eliminar un temporizador; cambiar el punto de entrada predeterminado y el punto de entrada del temporizador.
Devolverá 0 si no ha habido error, 1 si no es posible habilitar un temporizador ó 15 (hex 0xF) si no existe el hilo indicado en nThreadID.
Formatos (C & VB) long ModifyThread(long nThreadID, [BOOL bToggleState = 0], [long FuncAdd = 0],[long nNewInterval = 0], [long AutoFuncAdd = 0]) ModifyThread(nThreadID As long, [bToggleState As Boolean= 0],[FuncAdd As long = 0], [nNewInterval As long= 0], [AutoFuncAdd As long = 0])
nThreadID, ID del hilo a modificar.
bToggleState, alternar modo recursivo/no recursivo.
FuncAdd, nuevo punto de entrada predeterminado, (vea nota - vea al pie).
nNewInterval, nuevo intervalo del temporizador, (vea nota).
AutoFuncAdd, nuevo punto de entrada para el temporizador, (vea al pie).
Los valores indicados tanto en FuncAdd como en AutoFuncAddserán interpretados de la siguiente manera:
- (-1), no se realizarán cambios. - (0), deshabilitar punto de entrada, ahora se llamará al evento por defecto ThreadFunc. - cualquier otro valor será asignado como nuevo punto de entrada.
Si el valor indicado como punto de entrada en algún parámetro es erróneo recibirá un evento, indicando la causa y la acción realizada, (vea nota).
Instalar el software
El producto precisa introducir cierta información de configuración imprescindible para funcionar de manera adecuada en el registro de sistema. Para ello, se suministran los programas y/o scripts necesarios para una correcta instalación del mismo.
Éstos se describen a continuación:
Edición developer Pinche sobre el acceso directo regAx para instalar el software, o sobre unregAx para eliminarlo.
Edición enterprise Instalar un servidor Ejecute el programa sv_inst.exe y siga las indicaciones del mismo. NOTA: El servicio debe ser instalado en un equipo ejecutando NT/2000, por lo que no le será posible instalarlo en equipos que ejecuten otras plataformas. Instalar un cliente Ejecute el programa cl_inst.exe y siga las indicaciones del mismo. NOTA: El programa de instalación solicitará información sobre el equipo servidor. Debe introducir aquí nombre o dirección IP del mismo.
Desinstalar Abra el applet Agregar o quitar programas del Panel de Control y elija
- Administrador de Licencias AxIThread 4.0 para eliminar el servidor, o - Componentes AxIThread 4.0 para eliminar el cliente.
Registrarse
No voy a repetirle lo que es shareware, si lo desea puede leer la nota al final del texto. Pero si algunos apuntes de mi manera de ver las cosas.
En primer lugar, al diseñar esta librería he pensado en facilitar el uso de características interesantes de los ‘modernos’ sistemas operativos, que de otro modo sólo son accesibles por medio de continuas llamadas al API de Windooze. Considero que lo encontrará práctico y fácil de usar.
Me ha llevado un tiempo y me he tomado un trabajo hasta que he quedado satisfecho del funcionamiento de un componente del que espero sirva de ayuda en sus desarrollos.
Por otro lado, trato de ganarme la vida con esto, con lo cual debo buscar una manera de compersar el trabajo que me tomo, y las horas de ‘autismo’ que soportan quienes viven conmigo.
No soy partidario de facilitar componentes con características mermadas o limitadas en el tiempo, así que AxIThread es totalmente funcional. Pero considero que quienes se registran deben disfrutar de alguna ventaja, por pequeña que esta sea, frente a quienes no lo hacen. En este caso, la única ventaja de registrarse (aparte de contribuir a mantener la filosofía shareware) será eliminar la ventana que aparece cuando la librería se carga en memoria.
Para registrarse notifíquelo en la dirección cmaestra@arrakis.es, indicando los siguientes datos de registro:
Nombre (ya sea particular o comercial). Dirección de correo (eMail). Número de licencias (sólo licencias empresariales).
No olvide indicar la versión a registrar (developer o enterprise).
La forma de pago será (preferiblemente) por transferencia bancaria a la cuenta:
2038-4028-57-6000036475
o bien por giro postal o cheque pagaderos a:
Miguel Perancho Hevia San Bartolomeo da Freixa 32514 - Boborás, Ourense España
indicando en cualquier caso como concepto el nombre usado para registrarse. Una vez hecho efectivo, recibirá el número de licencia para registrar el componente en la dirección solicitada.
Lista de precios: (Ene-Dic 2001)
AxIThread, edición developer, 12.500 pts, 75 euro, $75 US
AxIThread, edición enterprise,
3 primeras licencias, 78.000 pts, 470 euro, $470 US
licencias adicionales
3-9, 7.500 pts, 45 euro, $45 US (unidad)
10-25, 6.000 pts, 36 euro, $36 US (unidad)
25-100, 4.000 pts, 24 euro, $24 US (unidad)
100-?, consultar
AxIThread, centros de enseñanza,
Consultar para condiciones especiales
Acerca del shareware
Hasta hace bien poco, los programas comerciales se compraban prácticamente a ciegas, siendo después de la compra cuando evaluábamos el producto; a no ser que, ante la perspectiva de un volumen grande de ventas, la empresa responsable del desarrollo o comercialización de la misma, se aviniese a hacernos una demostración. Hoy en día es más habitual que, de manera anterior al lanzamiento de la versión final de un programa, pase por nuestras manos una 'pre-Release', 'Demo' o 'Beta' del mismo con características recortadas o limitada en el tiempo con el fin de evaluar su funcionamiento y/o rendimiento. El auge del shareware tiene bastante que ver con esto.
Ya ha pasado el tiempo en que el shareware y el freeware era software de menor calidad que los programas comerciales; como muestra puede probar cualquiera de los programas share de diseño o retoque que seguramente pueda instalar desde el cd-rom que le regala su revista favorita.
Como bien sabe la filosofía sobre la que se apoya el shareware es la de ‘probar antes de comprar’. Así, frente al programa comercial, un usuario comprueba las excelencias o carencias del software antes de tomar una decisión sobre la adquisición del mismo. Y en este caso, el desembolso exigido es menor. También es cierto (aunque no siempre) que un programador, o pequeño grupo de programadores, no puede competir frente a un equipo formado por numerosas personas y presupuestos millonarios a la hora de diseñar proyectos complejos. En cambio, sí puede ofrecer otro tipo de soluciones (ya sean componentes, add-ins o programas completos) de utilidad para el resto de usuarios.
Las personas que pasamos largos ratos delante de la dichosa maquinita, en ocasiones nos topamos con que al fabuloso programa ,que ocupa 200 ricos megas de nuestro disco duro y que tanto y tan bien hace, le falta algun detalle que nos facilitaría el trabajo en gran medida, detalle al que algún programador, en alguna parte del mundo, le ha encontrado solución y nos la ofrece a cambio de una compensación a su dedicación, generalmente poco onerosa.
El shareware se basa en la confianza que deposita el programador de una pequeña utilidad que nos ahorra algún esfuerzo en que nosotros compensemos el suyo. No es raro encontrar aquellos que sólo piden que les enviemos una tarjeta informando de quienes somos, dónde nos encontramos y que nos parece su trabajo, aunque es más habitual, y yo en este caso así lo he decidido, que la compensación sea económica.
Esto es shareware. Yo ofrezco algo que cualquiera puede tomar y probar cuanto guste. Si ese alguien, después de probarlo, decide usarlo en la práctica, es libre de hacerlo siempre y cuando cumpla las condiciones en que se lo ofrezco.
Todo esto no sirve de nada sin el apoyo de los usuarios. Si nadie se registrase, el programador dejaría de desarrollar para el usuario en general, y quedaríamos en manos de los de siempre. Las ventajas de apoyar el shareware son obvias: disponer de una ingente cantidad de software frente al (generalmente más caro, aunque generalmente también más completo) software 'comercial'.
Pero al menos tendrá la oportunidad de elegir entre muchas posibilidades y no verse forzado a escoger entre unos pocos.
Ahora, usted es quien decide...
El autor (o culpable)
|
|