Execução assíncrona (Método de Sondagem)
Antes do ODBC 3.8 e do SDK do Windows 7, as operações assíncronas eram permitidas apenas em funções de instrução. Para obter mais informações, consulte Executar operações de instrução de forma assíncrona mais adiante neste tópico.
O ODBC 3.8 no SDK do Windows 7 introduziu a execução assíncrona em operações relacionadas à conexão. Para obter mais informações, consulte a seção Executar operações de conexão de forma assíncrona mais adiante neste tópico.
No SDK do Windows 7, para operações de conexão ou de instrução assíncrona, um aplicativo usava o método de sondagem para determinar que a operação assíncrona estava concluída. A partir do SDK do Windows 8, você pode determinar se uma operação assíncrona está concluída usando o método de notificação. Para obter mais informações, consulte Execução assíncrona (método de notificação).
Por padrão, os drivers executam funções ODBC de forma síncrona, ou seja, o aplicativo chama uma função e o driver não retorna o controle para o aplicativo até que ele tenha terminado de executar a função. No entanto, algumas funções pode ser executadas de forma assíncrona, ou seja, o aplicativo chama a função e o driver, após processamento mínimo, devolve o controle ao aplicativo. Enquanto a primeira função ainda está em execução, o aplicativo pode chamar outras funções.
A execução assíncrona é compatível com a maioria das funções que são amplamente executadas na fonte de dados, como as funções que estabelecem conexões, preparam e executam instruções SQL, recuperam metadados, buscam dados e confirmam transações. Essa execução é mais útil quando a tarefa que está sendo executada na fonte de dados é demorada, como um processo de logon ou uma consulta complexa em um banco de dados grande.
Quando o aplicativo executa uma função com uma instrução ou conexão habilitada para processamento assíncrono, o driver executa uma quantidade mínima de processamento (como a verificação de argumentos em busca de erros), entrega o processamento à fonte de dados e devolve o controle ao aplicativo com o código de retorno SQL_STILL_EXECUTING. O aplicativo então realiza outras tarefas. Para determinar quando a função assíncrona é concluída, o aplicativo faz a sondagem do driver em intervalos regulares, chamando a função com os mesmos argumentos usados originalmente. Se a função ainda estiver em execução, ela retornará SQL_STILL_EXECUTING. Se a execução tiver sido concluída, ela retornará o código que retornaria se tivesse sido executada de forma síncrona, como SQL_SUCCESS, SQL_ERROR ou SQL_NEED_DATA.
O driver especifica se uma função é executada de forma síncrona ou assíncrona. Por exemplo, suponha que os metadados do conjunto de resultados estejam armazenados em cache no driver. Neste caso, executar o SQLDescribeCol é muito rápido e o driver deve simplesmente executar a função em vez de atrasar artificialmente a execução. Por outro lado, se o driver precisar recuperar os metadados da fonte de dados, ele deve devolver o controle ao aplicativo durante esse processo. Portanto, o aplicativo deve ser capaz de identificar um código de retorno diferente de SQL_STILL_EXECUTING na primeira vez que executar uma função de forma assíncrona.
Executar operações de instrução de forma assíncrona
As funções de instrução a seguir funcionam em uma fonte de dados e podem ser executadas de forma assíncrona:
SQLBulkOperations
SQLColAttribute
SQLColumnPrivileges
SQLColumns
SQLDescribeCol
SQLDescribeParam
SQLExecDirect
SQLExecute
SQLFetch
A execução da instrução de forma assíncrona é controlada por instrução ou por conexão, dependendo da fonte de dados. Ou seja, o aplicativo especifica não que uma determinada função deve ser executada de forma assíncrona, mas que qualquer função executada em uma instrução específica deve ser executada de forma assíncrona. Para descobrir qual das opções é compatível, um aplicativo chama SQLGetInfo com uma opção de SQL_ASYNC_MODE. O SQL_AM_CONNECTION retorna se uma execução assíncrona no nível da conexão (para um identificador de instrução) for compatível. Se uma execução assíncrona no nível da instrução é compatível, SQL_AM_STATEMENT é retornado.
Para especificar que as funções executadas com uma instrução específica devem ser executadas de forma assíncrona, o aplicativo chama SQLSetStmtAttr com o atributo SQL_ATTR_ASYNC_ENABLE e o define para SQL_ASYNC_ENABLE_ON. Se houver suporte ao processamento assíncrono no nível da conexão, o atributo da instrução SQL_ATTR_ASYNC_ENABLE será somente leitura e seu valor será o mesmo que o atributo de conexão da conexão na qual a instrução foi alocada. A definição do valor do atributo de instrução no momento da alocação da instrução ou posteriormente é especificada pelo driver. A tentativa de defini-lo retornará SQL_ERROR e SQLSTATE HYC00 (Recurso opcional não implementado).
Para especificar que as funções executadas com uma conexão específica devem ser executadas de forma assíncrona, o aplicativo chama SQLSetConnectAttr com o atributo SQL_ATTR_ASYNC_ENABLE e o define para SQL_ASYNC_ENABLE_ON. Todos os identificadores futuros de instrução alocados na conexão serão habilitados para execução assíncrona. O driver define se os identificadores de instrução existentes serão habilitados por esta ação. Se SQL_ATTR_ASYNC_ENABLE estiver definido como SQL_ASYNC_ENABLE_OFF, todas as instruções na conexão estarão no modo síncrono. Um erro será retornado se a execução assíncrona estiver habilitada enquanto houver uma instrução ativa na conexão.
Para determinar o número máximo de instruções simultâneas ativas modo assíncrono que o driver pode oferecer suporte em uma determinada conexão, o aplicativo chama SQLGetInfo com a opção SQL_MAX_ASYNC_CONCURRENT_STATEMENTS.
O código a seguir demonstra como o modelo de sondagem funciona:
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.
Enquanto uma função está sendo executada de forma assíncrona, o aplicativo pode chamar funções em qualquer outra instrução. O aplicativo também pode chamar funções em qualquer conexão, exceto a que está associada à instrução assíncrona. No entanto, o aplicativo só pode chamar a função original e as funções seguintes (com o identificador da instrução ou sua conexão associada, identificador de ambiente) depois que uma operação de instrução retornar SQL_STILL_EXECUTING.
SQLCancelHandle (no identificador de instrução)
Se o aplicativo chamar qualquer outra função com a instrução assíncrona ou com a conexão associada a essa instrução, a função retornará SQLSTATE HY010 (erro de sequência de função), por exemplo.
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
}
Quando um aplicativo chama uma função para determinar se ela ainda está sendo executada de forma assíncrona, ele deve usar o identificador original da instrução. Isso ocorre porque a execução assíncrona é controlada por instrução. O aplicativo também deve fornecer valores válidos para os outros argumentos (como os argumentos originais farão) para passar na verificação de erros no Gerenciador de Driver. No entanto, depois que o driver verifica o identificador da instrução e determina que a instrução está em execução assíncrona, ele ignora todas as outras instruções.
Enquanto uma função é executada de forma assíncrona (ou seja, depois de retornar SQL_STILL_EXECUTING e antes de retornar um código diferente), o aplicativo pode cancelá-la chamando SQLCancel ou SQLCancelHandle com o mesmo identificador de instrução. Isso não garante o cancelamento da execução da função. Por exemplo, a função pode já ter terminado. Além disso, o código retornado por SQLCancel ou SQLCancelHandle indica apenas se a tentativa de cancelar a função foi bem-sucedida, não se ela realmente cancelou a função. Para determinar se a função foi cancelada, o aplicativo chama a função novamente. Se a função foi cancelada, ela retorna SQL_ERROR e SQLSTATE HY008 (Operação cancelada). Se a função não foi cancelada, ela retorna outro código, como SQL_SUCCESS, SQL_STILL_EXECUTING ou SQL_ERROR com um SQLSTATE diferente.
Para desabilitar a execução assíncrona de uma instrução específica quando o driver oferecer suporte ao processamento assíncrono no nível de instrução, o aplicativo chama SQLSetStmtAttr com o atributo SQL_ATTR_ASYNC_ENABLE e o define como SQL_ASYNC_ENABLE_OFF. Se o driver oferecer suporte ao processamento assíncrono no nível de conexão, o aplicativo chamará SQLSetConnectAttr para definir SQL_ATTR_ASYNC_ENABLE como SQL_ASYNC_ENABLE_OFF, o que desabilitará a execução assíncrona de todas as instruções na conexão.
O aplicativo deve processar os registros de diagnóstico no loop de repetição da função original. Se SQLGetDiagField ou SQLGetDiagRec for chamado quando uma função assíncrona estiver em execução, ele retornará a lista atual dos registros de diagnóstico. Sempre que a chamada de função original é repetida, ela limpa os registros de diagnóstico anteriores.
Executar operações de conexão de forma assíncrona
Antes do ODBC 3.8, a execução assíncrona era permitida para operações relacionadas a instruções, como preparar, executar e efetuar fetch, bem como para operações de metadados de catálogo. A partir do ODBC 3.8, a execução assíncrona também é possível para operações relacionadas à conexão, como conectar, desconectar, fazer commit e reversão.
Para obter mais informações sobre o ODBC 3.8, consulte Novidades do ODBC 3.8.
Executar operações de conexão de forma assíncrona é útil nos seguintes cenários:
Quando poucas threads gerenciam um número grande de dispositivos com taxas de dados muito altas. Para maximizar a responsividade e a escalabilidade, é aconselhável que todas as operações sejam assíncronas.
Quando você deseja sobrepor operações do banco de dados em várias conexões para reduzir os tempos de transferência decorridos.
Chamadas ODBC assíncronas eficientes e a capacidade de cancelar operações de conexão permitem que um aplicativo permita que o usuário cancele qualquer operação lenta sem ter que esperar pelo tempo limite.
As seguintes funções, que operam em identificadores de conexão, agora podem ser executadas de forma assíncrona:
Para determinar se um driver é compatível com operações assíncronas nessa funções, o aplicativo chama SQLGetInfo com SQL_ASYNC_DBC_FUNCTIONS. SQL_ASYNC_DBC_CAPABLE será retornado se operações assíncronas forem compatíveis. SQL_ASYNC_DBC_NOT_CAPABLE será retornado se operações assíncronas não forem compatíveis.
Para especificar que as funções executadas com uma conexão específica devem ser executadas de forma assíncrona, o aplicativo chama SQLSetConnectAttr e define o atributo SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE como SQL_ASYNC_DBC_ENABLE_ON. Definir um atributo de conexão antes de estabelecer uma conexão sempre é executado de forma assíncrona. Além disso, a operação que define o atributo de conexão SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE com SQLSetConnectAttr sempre é executada de forma síncrona.
Um aplicativo pode habilitar a operação assíncrona antes de fazer uma conexão. Como o Gerenciador de Driver não determina qual driver usar antes de fazer uma conexão, ele sempre retornará SQLSetConnectAttr com êxito. No entanto, pode haver falha na conexão se o driver ODBC não for compatível com operações assíncronas.
Em geral, um identificador de conexão ou de instrução específico pode ter no máximo uma função de execução assíncrona associada. No entanto, um identificador de conexão pode ter mais de um identificador de instrução associado. Se não houver nenhuma operação assíncrona em execução no identificador de conexão, um identificador de instrução associado poderá executar uma operação assíncrona. Da mesma forma, é possível ter uma operação assíncrona em um identificador de conexão se não houver operações assíncronas em andamento em qualquer identificador de instrução associado. Uma tentativa de executar uma operação assíncrona usando um identificador que está executando uma operação assíncrona retornará HY010, "Erro de sequência de função".
Se uma operação de conexão retornar SQL_STILL_EXECUTING, um aplicativo só poderá chamar a função original e as seguintes funções para esse identificador de conexão:
SQLCancelHandle (no identificador de conexão)
SQLGetDiagField
SQLGetDiagRec
SQLAllocHandle (alocando ENV/DBC)
SQLAllocHandleStd (alocando ENV/DBC)
SQLGetEnvAttr
SQLGetConnectAttr
SQLDataSources
SQLDrivers
SQLGetInfo
SQLGetFunctions
O aplicativo deve processar os registros de diagnóstico no loop de repetição da função original. Se SQLGetDiagField ou SQLGetDiagRec for chamado quando uma função assíncrona estiver em execução, ele retornará a lista atual de registros de diagnóstico. Sempre que a chamada de função original é repetida, ela limpa os registros de diagnóstico anteriores.
Se uma conexão for aberta ou fechada de forma assíncrona, a operação é concluída quando o aplicativo recebe SQL_SUCCESS ou SQL_SUCCESS_WITH_INFO na chamada de função original.
Uma nova função foi adicionada ao ODBC 3.8, SQLCancelHandle. Essa função cancela as seis funções de conexão (SQLBrowseConnect, SQLConnect, SQLDisconnect, SQLDriverConnect, SQLEndTran e SQLSetConnectAttr). Um aplicativo deve chamar SQLGetFunctions para determinar se o driver oferece suporte a SQLCancelHandle. Assim como em SQLCancel, se SQLCancelHandle retornar êxito, não significa que a operação foi cancelada. Um aplicativo deve chamar a função original novamente para determinar se a operação foi cancelada. SQLCancelHandle permite cancelar operações assíncronas em identificadores de conexão ou de instrução. Usar SQLCancelHandle para cancelar uma operação em um identificador de instrução é o mesmo que chamar SQLCancel.
Não é necessário oferecer suporte a operações de conexão assíncronas e a SQLCancelHandle ao mesmo tempo. Um driver pode ser compatível com operações de conexão assíncronas, mas não com SQLCancelHandle ou vice-versa.
Operações de conexão assíncronas e SQLCancelHandle também podem ser usadas por aplicativos com ODBC 3.x e ODBC 2.x com um driver ODBC 3.8 e Gerenciador de Driver ODBC 3.8. Para obter informações sobre como habilitar um aplicativo mais antigo para usar novos recursos em uma versão posterior do ODBC, consulte Matriz de compatibilidade.
Pool de conexões
Sempre que um pool de conexões está habilitado, as operações assíncronas têm suporte mínimo para estabelecer uma conexão (com SQLConnect e SQLDriverConnect) e fechar uma conexão com SQLDisconnect. No entanto, um aplicativo ainda é capaz de identificar o valor retornado SQL_STILL_EXECUTING de SQLConnect, SQLDriverConnect e SQLDisconnect.
Quando o pool de conexões está habilitado, SQLEndTran e SQLSetConnectAttr têm suporte para operações assíncronas.
Exemplos
R. Habilitar a execução assíncrona de funções de conexão
O exemplo a seguir mostra como usar SQLSetConnectAttr para habilitar a execução assíncrona para funções relacionadas à conexão.
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. Operações de commit assíncronas
Este exemplo mostra operações de commit assíncronas. As operações de reversão também podem ser feitas dessa forma.
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;
}