Share via


Ejecución asincrónica (método de sondeo)

Antes de ODBC 3.8 y el SDK para Windows 7, solo se permitían las operaciones asincrónicas en las funciones de instrucción. Para obtener más información, consulte Ejecutar operaciones de instrucciones de forma asincrónica, más adelante en este tema.

En el SDK para Windows 7, ODBC 3.8 introdujo la ejecución asincrónica en las operaciones relacionadas con la conexión. Para obtener más información, consulte la sección Ejecutar operaciones de conexión de forma asincrónica, más adelante en este tema.

En el SDK para Windows 7, para las operaciones de conexión o instrucciones asincrónicas, una aplicación determinó que la operación asincrónica se completó mediante el método de sondeo. A partir del SDK para Windows 8, puedes determinar si una operación asincrónica se ha completado mediante el método de notificación. Para obtener más información, consulte Ejecución asincrónica (método de notificación).

De manera predeterminada, los controladores ejecutan funciones de ODBC de forma sincrónica; es decir, la aplicación llama a una función y el controlador no devuelve el control a la aplicación hasta que haya terminado de ejecutar la función. Sin embargo, algunas funciones se pueden ejecutar de forma asincrónica; es decir, la aplicación llama a la función y el controlador, después del procesamiento mínimo, devuelve el control a la aplicación. A continuación, la aplicación puede llamar a otras funciones mientras la primera función sigue ejecutándose.

La ejecución asincrónica se admite para la mayoría de las funciones que se ejecutan en gran medida en el origen de datos, como las funciones para establecer conexiones, preparar y ejecutar instrucciones SQL, recuperar metadatos, capturar datos y confirmar transacciones. Resulta más útil cuando la tarea que se ejecuta en el origen de datos tarda mucho tiempo, como un proceso de inicio de sesión o una consulta compleja en una base de datos grande.

Cuando la aplicación ejecuta una función con una instrucción o conexión que está habilitada para el procesamiento asincrónico, el controlador realiza una cantidad mínima de procesamiento (como comprobar argumentos de errores), entrega el procesamiento al origen de datos y devuelve el control a la aplicación con el código de retorno de SQL_STILL_EXECUTING. A continuación, la aplicación realiza las demás tareas. Para determinar cuándo ha finalizado la función asincrónica, la aplicación sondea el controlador a intervalos regulares llamando a la función con los mismos argumentos que usó originalmente. Si la función sigue ejecutándose, devuelve SQL_STILL_EXECUTING; si ha terminado de ejecutarse, devuelve el código que habría devuelto si se hubiera ejecutado de forma sincrónica, como SQL_SUCCESS, SQL_ERROR o SQL_NEED_DATA.

El hecho de que una función se ejecute de forma sincrónica o asincrónica depende del controlador. Por ejemplo, supongamos que los metadatos del conjunto de resultados se almacenan en caché en el controlador. En este caso, se tarda muy poco tiempo en ejecutar SQLDescribeCol y el controlador simplemente debe ejecutar la función en lugar de retrasar la ejecución artificialmente. Por otro lado, si el controlador necesita recuperar los metadatos del origen de datos, debe devolver el control a la aplicación mientras lo hace. Por lo tanto, la aplicación debe ser capaz de controlar un código de retorno distinto de SQL_STILL_EXECUTING cuando ejecuta por primera vez una función de forma asincrónica.

Ejecutar operaciones de instrucciones de forma asincrónica

Las siguientes funciones de instrucción funcionan en un origen de datos y se pueden ejecutar de forma asincrónica:

La ejecución de instrucciones asincrónicas se controla por instrucción o por conexión, en función del origen de datos. Es decir, la aplicación no especifica que una función determinada se debe ejecutar de forma asincrónica, sino que cualquier función ejecutada en una instrucción determinada se debe ejecutar de forma asincrónica. Para averiguar cuál es compatible, una aplicación llama a SQLGetInfo con una opción de SQL_ASYNC_MODE. SQL_AM_CONNECTION se devuelve si se admite la ejecución asincrónica de nivel de conexión (para un identificador de instrucción); SQL_AM_STATEMENT si se admite la ejecución asincrónica de nivel de instrucción.

Para especificar que las funciones ejecutadas con una instrucción determinada se deben ejecutar de forma asincrónica, la aplicación llama a SQLSetStmtAttr con el atributo SQL_ATTR_ASYNC_ENABLE y lo establece en SQL_ASYNC_ENABLE_ON. Si se admite el procesamiento asincrónico de nivel de conexión, el atributo de instrucción SQL_ATTR_ASYNC_ENABLE es de solo lectura y su valor es el mismo que el atributo de conexión de la conexión en la que se asignó la instrucción. Depende del controlador si el valor del atributo de instrucción se establece en el momento de la asignación de instrucción o posteriormente. Al intentar establecerlo, se devolverán SQL_ERROR y SQLSTATE HYC00 (característica opcional no implementada).

Para especificar que las funciones ejecutadas con una conexión determinada se deben ejecutar de forma asincrónica, la aplicación llama a SQLSetConnectAttr con el atributo SQL_ATTR_ASYNC_ENABLE y lo establece en SQL_ASYNC_ENABLE_ON. Todos los identificadores de instrucción futuros asignados en la conexión se habilitarán para la ejecución asincrónica; el controlador define si esta acción habilitará los identificadores de instrucción existentes. Si SQL_ATTR_ASYNC_ENABLE se establece en SQL_ASYNC_ENABLE_OFF, todas las instrucciones de la conexión están en modo sincrónico. Se devuelve un error si la ejecución asincrónica está habilitada mientras hay una instrucción activa en la conexión.

Para determinar el número máximo de instrucciones simultáneas activas en modo asincrónico que el controlador puede admitir en una conexión determinada, la aplicación llama a SQLGetInfo con la opción SQL_MAX_ASYNC_CONCURRENT_STATEMENTS.

En el código siguiente se muestra cómo funciona el modelo de sondeo:

SQLHSTMT  hstmt1;  
SQLRETURN rc;  
  
// Specify that the statement is to be executed asynchronously.  
SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON, 0);  
  
// Execute a SELECT statement asynchronously.  
while ((rc=SQLExecDirect(hstmt1,"SELECT * FROM Orders",SQL_NTS))==SQL_STILL_EXECUTING) {  
   // While the statement is still executing, do something else.  
   // Do not use hstmt1, because it is being used asynchronously.  
}  
  
// When the statement has finished executing, retrieve the results.  

Mientras que una función se ejecuta de forma asincrónica, la aplicación puede llamar a funciones en cualquier otra instrucción. La aplicación también puede llamar a funciones en cualquier conexión, excepto la asociada a la instrucción asincrónica. Pero la aplicación solo puede llamar a la función original y a las siguientes funciones (con el identificador de instrucción o su conexión asociada, identificador de entorno) después de que una operación de instrucción devuelva SQL_STILL_EXECUTING:

Si la aplicación llama a cualquier otra función con la instrucción asincrónica o con la conexión asociada a esa instrucción, la función devuelve SQLSTATE HY010 (error de secuencia de funciones), por ejemplo.

SQLHDBC       hdbc1, hdbc2;  
SQLHSTMT      hstmt1, hstmt2, hstmt3;  
SQLCHAR *     SQLStatement = "SELECT * FROM Orders";  
SQLUINTEGER   InfoValue;  
SQLRETURN     rc;  
  
SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1);  
SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt2);  
SQLAllocHandle(SQL_HANDLE_STMT, hdbc2, &hstmt3);  
  
// Specify that hstmt1 is to be executed asynchronously.  
SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON, 0);  
  
// Execute hstmt1 asynchronously.  
while ((rc = SQLExecDirect(hstmt1, SQLStatement, SQL_NTS)) == SQL_STILL_EXECUTING) {  
   // The following calls return HY010 because the previous call to  
   // SQLExecDirect is still executing asynchronously on hstmt1. The  
   // first call uses hstmt1 and the second call uses hdbc1, on which  
   // hstmt1 is allocated.  
   SQLExecDirect(hstmt1, SQLStatement, SQL_NTS);   // Error!  
   SQLGetInfo(hdbc1, SQL_UNION, (SQLPOINTER) &InfoValue, 0, NULL);   // Error!  
  
   // The following calls do not return errors. They use a statement  
   // handle other than hstmt1 or a connection handle other than hdbc1.  
   SQLExecDirect(hstmt2, SQLStatement, SQL_NTS);   // OK  
   SQLTables(hstmt3, NULL, 0, NULL, 0, NULL, 0, NULL, 0);   // OK  
   SQLGetInfo(hdbc2, SQL_UNION, (SQLPOINTER) &InfoValue, 0, NULL);   // OK  
}  

Cuando una aplicación llama a una función para determinar si todavía se está ejecutando de forma asincrónica, debe usar el identificador de instrucción original. Esto se debe a que se realiza un seguimiento de la ejecución asincrónica por instrucción. La aplicación también debe proporcionar valores válidos para los otros argumentos (los argumentos originales ya servirán) para superar la comprobación de errores en el Administrador de controladores. Sin embargo, después de que el controlador compruebe el identificador de instrucción y determine que la instrucción se está ejecutando de forma asincrónica, ignora todos los demás argumentos.

Mientras una función se ejecuta de forma asincrónica, es decir, después de que haya devuelto SQL_STILL_EXECUTING y antes de que devuelva un código diferente, la aplicación puede cancelarla llamando a SQLCancel o SQLCancelHandle con el mismo identificador de instrucción. No está garantizado que esta acción cancele la ejecución de la función. Por ejemplo, es posible que la función ya haya terminado. Además, el código devuelto por SQLCancel o SQLCancelHandle solo indica si el intento de cancelar la función se realizó correctamente, no si realmente canceló la función. Para determinar si la función se canceló, la aplicación vuelve a llamarla. Si se canceló la función, devuelve SQL_ERROR y SQLSTATE HY008 (operación cancelada). Si no se canceló la función, devuelve otro código, como SQL_SUCCESS, SQL_STILL_EXECUTING o SQL_ERROR con un valor SQLSTATE diferente.

Para deshabilitar la ejecución asincrónica de una instrucción determinada cuando el controlador admite el procesamiento asincrónico de nivel de instrucción, la aplicación llama a SQLSetStmtAttr con el atributo SQL_ATTR_ASYNC_ENABLE y lo establece en SQL_ASYNC_ENABLE_OFF. Si el controlador admite el procesamiento asincrónico de nivel de conexión, la aplicación llama a SQLSetConnectAttr para establecer SQL_ATTR_ASYNC_ENABLE en SQL_ASYNC_ENABLE_OFF, lo que deshabilita la ejecución asincrónica de todas las instrucciones en la conexión.

La aplicación debe procesar los registros de diagnóstico en el bucle de repetición de la función original. Si se llama a SQLGetDiagField o SQLGetDiagRec cuando se ejecuta una función asincrónica, devolverá la lista actual de registros de diagnóstico. Cada vez que se repite la llamada de función original, se borran los registros de diagnóstico anteriores.

Ejecutar operaciones de conexión de forma asincrónica

Antes de ODBC 3.8, se permitía la ejecución asincrónica en las operaciones relacionadas con instrucciones, como preparar, ejecutar y capturar, así como en las operaciones de metadatos del catálogo. A partir de ODBC 3.8, la ejecución asincrónica también es posible en las operaciones relacionadas con la conexión, como la conexión, la desconexión, la confirmación y la reversión.

Para más información sobre ODBC 3.8, consulte Novedades de ODBC 3.8.

La ejecución de operaciones de conexión de forma asincrónica es útil en los escenarios siguientes:

  • Cuando un pequeño número de subprocesos administra un gran número de dispositivos con tasas de datos muy altas. Para maximizar la capacidad de respuesta y la escalabilidad, es deseable que todas las operaciones sean asincrónicas.

  • Si desea superponer las operaciones de base de datos en varias conexiones para reducir los tiempos de transferencia transcurridos.

  • Con las llamadas de ODBC asincrónicas eficaces y la capacidad de cancelar las operaciones de conexión, la aplicación permite al usuario cancelar cualquier operación lenta sin tener que esperar que se agote el tiempo de espera.

Las siguientes funciones, que funcionan en identificadores de conexión, ahora se pueden ejecutar de forma asincrónica:

Para determinar si un controlador admite operaciones asincrónicas en estas funciones, una aplicación llama a SQLGetInfo con SQL_ASYNC_DBC_FUNCTIONS. Se devuelve SQL_ASYNC_DBC_CAPABLE si se admiten las operaciones asincrónicas. Se devuelve SQL_ASYNC_DBC_NOT_CAPABLE si no se admiten las operaciones asincrónicas.

Para especificar que las funciones ejecutadas con una conexión determinada se deben ejecutar de forma asincrónica, la aplicación llama a SQLSetConnectAttr y establece el atribtuo SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE en SQL_ASYNC_DBC_ENABLE_ON. Establecer un atributo de conexión antes de establecer una conexión siempre se ejecuta de forma sincrónica. Además, la operación que establece el atributo de conexión SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE con SQLSetConnectAttr siempre se ejecuta de forma sincrónica.

Una aplicación puede habilitar la operación asincrónica antes de realizar una conexión. Dado que el Administrador de controladores no puede determinar qué controlador usar antes de realizar una conexión, siempre devolverá el éxito en SQLSetConnectAttr. Sin embargo, podría no conectarse si el controlador ODBC no admite las operaciones asincrónicas.

En general, puede haber como máximo una función de ejecución asincrónica asociada a un identificador de conexión o identificador de instrucción determinado. Sin embargo, un identificador de conexión puede tener más de un identificador de instrucción asociado. Si no hay ninguna operación asincrónica que se ejecute en el identificador de conexión, un identificador de instrucción asociado puede ejecutar una operación asincrónica. De forma similar, puede tener una operación asincrónica en un identificador de conexión si no hay ninguna operación asincrónica en curso en ningún identificador de instrucción asociado. Si intenta ejecutar una operación asincrónica mediante un identificador que està ejecutando una operación asincrónica, se devolverá HY010 (error de secuencia de funciones).

Si una operación de conexión devuelve SQL_STILL_EXECUTING, una aplicación solo puede llamar a la función original y a las siguientes funciones para ese identificador de conexión:

  • SQLCancelHandle (en el identificador de conexión)

  • SQLGetDiagField

  • SQLGetDiagRec

  • SQLAllocHandle (asignación de ENV/DBC)

  • SQLAllocHandleStd (asignación de ENV/DBC)

  • SQLGetEnvAttr

  • SQLGetConnectAttr

  • SQLDataSources

  • SQLDrivers

  • SQLGetInfo

  • SQLGetFunctions

La aplicación debe procesar los registros de diagnóstico en el bucle de repetición de la función original. Si se llama a SQLGetDiagField o SQLGetDiagRec cuando se ejecuta una función asincrónica, devolverá la lista actual de registros de diagnóstico. Cada vez que se repite la llamada de función original, se borran los registros de diagnóstico anteriores.

Si se abre o cierra una conexión de forma asincrónica, la operación se completa cuando la aplicación recibe SQL_SUCCESS o SQL_SUCCESS_WITH_INFO en la llamada de función original.

Se ha agregado una nueva función a ODBC 3.8, SQLCancelHandle. Esta función cancela las seis funciones de conexión (SQLBrowseConnect, SQLConnect, SQLDisconnect, SQLDriverConnect, SQLEndTran y SQLSetConnectAttr). Una aplicación debe llamar a SQLGetFunctions para determinar si el controlador admite SQLCancelHandle. Al igual que con SQLCancel, si SQLCancelHandle devuelve que se ha completado correctamente, no significa que la operación se haya cancelado. Una aplicación debe llamar a la función original de nuevo para determinar si se canceló la operación. SQLCancelHandle permite cancelar operaciones asincrónicas en identificadores de conexión o identificadores de instrucción. El uso de SQLCancelHandle para cancelar una operación en un identificador de instrucción es el mismo que llamar a SQLCancel.

No es necesario admitir las operaciones de conexión asincrónica y SQLCancelHandle al mismo tiempo. Un controlador puede admitir operaciones de conexión asincrónicas, pero no SQLCancelHandle, o viceversa.

Las aplicaciones de ODBC 3.x y ODBC 2.x también pueden usar las operaciones de conexión asincrónicas y SQLCancelHandle con un controlador ODBC 3.8 y el Administrador de controladores ODBC 3.8. Para obtener información sobre cómo habilitar una aplicación anterior para usar nuevas características en las versiones recientes de ODBC, consulte Matriz de compatibilidad.

Agrupar conexiones

Cada vez que la agrupación de conexiones está habilitada, las operaciones asincrónicas solo se admiten mínimamente para establecer una conexión (con SQLConnect y SQLDriverConnect) y cerrar una conexión con SQLDisconnect. Pero una aplicación todavía debe poder controlar el valor devuelto SQL_STILL_EXECUTING de SQLConnect, SQLDriverConnect y SQLDisconnect.

Cuando la agrupación de conexiones está habilitada, SQLEndTran y SQLSetConnectAttr se admiten en las operaciones asincrónicas.

Ejemplos

A Habilitación de la ejecución asincrónica de funciones de conexión

En el ejemplo siguiente se muestra cómo usar SQLSetConnectAttr para habilitar la ejecución asincrónica en las funciones relacionadas con la conexión.

BOOL AsyncConnect (SQLHANDLE hdbc)   
{  
   SQLRETURN r;  
   SQLHANDLE hdbc;  
  
   // Enable asynchronous execution of connection functions.  
   // This must be executed synchronously, that is r != SQL_STILL_EXECUTING  
   r = SQLSetConnectAttr(  
         hdbc,   
         SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE,  
         reinterpret_cast<SQLPOINTER> (SQL_ASYNC_DBC_ENABLE_ON),  
         0);  
   if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)   
   {  
      return FALSE;  
   }  
  
   TCHAR szConnStrIn[256] = _T("DSN=AsyncDemo");  
  
   r = SQLDriverConnect(hdbc, NULL, (SQLTCHAR *) szConnStrIn, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);  
  
   if (r == SQL_ERROR)   
   {  
      // Use SQLGetDiagRec to process the error.  
      // If SQLState is HY114, the driver does not support asynchronous execution.  
      return FALSE;  
   }  
  
   while (r == SQL_STILL_EXECUTING)   
   {  
      // Do something else.  
  
      // Check for completion, with the same set of arguments.  
      r = SQLDriverConnect(hdbc, NULL, (SQLTCHAR *) szConnStrIn, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);  
   }  
  
   if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)   
   {  
      return FALSE;  
   }  
  
   return TRUE;  
}  
  

B. Operaciones de confirmación asincrónicas

En este ejemplo se muestran las operaciones de confirmación asincrónicas. Las operaciones de reversión también se pueden realizar de esta manera.

BOOL AsyncCommit ()   
{  
   SQLRETURN r;   
  
   // Assume that SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE is SQL_ASYNC_DBC_ENABLE_ON.  
  
   r = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);  
   while (r == SQL_STILL_EXECUTING)   
   {  
      // Do something else.  
  
      // Check for completion with the same set of arguments.  
      r = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);  
   }  
  
   if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)   
   {  
      return FALSE;  
   }  
   return TRUE;  
}  

Consulte también

Ejecución de instrucciones ODBC