Programación asíncrona con el AWS SDK para C++ - AWS SDK para C++

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Programación asíncrona con el AWS SDK para C++

Métodos asíncronos para el SDK

El SDK para C++ proporciona para muchos métodos versiones síncronas y asíncronas. Un método es asíncrono si incluye el sufijo Async en su nombre. Por ejemplo, el método PutObject de Amazon S3 es síncrono y PutObjectAsync es asíncrono.

Como todas las operaciones asíncronas, un método asíncrono del SDK devuelve resultados antes de que finalice su tarea principal. Por ejemplo, el método PutObjectAsync devuelve resultados antes de que termine de cargar el archivo en el bucket de Amazon S3. Mientras continúa la operación de carga, la aplicación puede realizar otras operaciones, incluido llamar a otros métodos asíncronos. La aplicación recibe una notificación de que una operación asíncrona ha finalizado cuando se invoca una función de devolución de llamada asociada.

En las siguientes secciones se describe un ejemplo de código que muestra la llamada al método asíncrono PutObjectAsync. Cada sección se centra en partes individuales del archivo de origen completo del ejemplo.

Llamada a métodos asíncronos del SDK

Por lo general, la versión asíncrona de un método del SDK acepta los siguientes argumentos.

  • Una referencia al mismo objeto de tipo Request que su homólogo síncrono.

  • Una referencia a una función de devolución de llamada del controlador de respuestas. Esta función de devolución de llamada se invoca cuando finaliza la operación asíncrona. Uno de los argumentos contiene el resultado de la operación.

  • Un shared_ptr opcional para un objeto AsyncCallerContext. El objeto se transfiere a la devolución de llamada del controlador de respuestas. Incluye una propiedad UUID que se puede usar para transferir información de texto a la devolución de llamada.

El método uploadFileAsync que se muestra a continuación configura y llama al método PutObjectAsync de Amazon S3 del SDK para cargar de forma asíncrona un archivo en un bucket de Amazon S3.

La función recibe referencias a un objeto S3Client y a un objeto PutObjectRequest. Las recibe de la función principal porque tenemos que asegurarnos de que estos objetos existan durante todas las llamadas asíncronas.

Se asigna un shared_ptr a un objeto AsyncCallerContext. Su propiedad UUID se establece en el nombre del objeto de Amazon S3. Solo para fines de demostración, la devolución de llamada del controlador de respuestas accede a la propiedad y genera su valor.

La llamada a PutObjectAsync incluye un argumento de referencia a la función de devolución de llamada del controlador de respuestas uploadFileAsyncFinished. Esta función de devolución de llamada se analiza con más detalle en la siguiente sección.

bool AwsDoc::S3::uploadFileAsync(const Aws::S3::S3Client &s3Client, Aws::S3::Model::PutObjectRequest &request, const Aws::String &bucketName, const Aws::String &fileName) { request.SetBucket(bucketName); request.SetKey(fileName); const std::shared_ptr<Aws::IOStream> input_data = Aws::MakeShared<Aws::FStream>("SampleAllocationTag", fileName.c_str(), std::ios_base::in | std::ios_base::binary); if (!*input_data) { std::cerr << "Error: unable to open file " << fileName << std::endl; return false; } request.SetBody(input_data); // Create and configure the context for the asynchronous put object request. std::shared_ptr<Aws::Client::AsyncCallerContext> context = Aws::MakeShared<Aws::Client::AsyncCallerContext>("PutObjectAllocationTag"); context->SetUUID(fileName); // Make the asynchronous put object call. Queue the request into a // thread executor and call the uploadFileAsyncFinished function when the // operation has finished. s3Client.PutObjectAsync(request, uploadFileAsyncFinished, context); return true; }

Los recursos de una operación asíncrona deben existir hasta que finalice la operación. Por ejemplo, los objetos de cliente y de solicitud deben existir hasta que la aplicación reciba la notificación de que se ha completado la operación. La aplicación en sí misma no puede finalizar hasta que se complete la operación asíncrona.

Por este motivo, el método uploadFileAsync acepta referencias a objetos S3Client y PutObjectRequest en lugar de crearlos en el método uploadFileAsync y almacenarlos en una variable local.

En el ejemplo, el método PutObjectAsync devuelve a la persona que llama inmediatamente después de iniciar la operación asíncrona, lo que permite a la cadena de llamada realizar tareas adicionales mientras la operación de carga está en curso.

Si el cliente estuviera almacenado en una variable local del método uploadFileAsync, quedaría fuera del alcance cuando el método devolviera un resultado. Sin embargo, el objeto de cliente debe seguir existiendo hasta finalizar la operación asincrónica.

Notificación de la finalización de una operación asíncrona

Cuando finaliza una operación asíncrona, se invoca una función de devolución de llamada del controlador de respuestas de la aplicación. Esta notificación incluye el resultado de la operación. El resultado está contenido en la misma clase de tipo Resultado que ha devuelto el método equivalente síncrono. En el ejemplo de código, el resultado está en un objeto PutObjectOutcome.

La función de devolución de llamada del controlador de respuestas del ejemplo uploadFileAsyncFinished se muestra a continuación. Comprueba si la operación asíncrona se realizó correctamente o no. Utiliza std::condition_variable para notificar al subproceso de la aplicación que la operación asíncrona ha finalizado.

// A mutex is a synchronization primitive that can be used to protect shared // data from being simultaneously accessed by multiple threads. std::mutex AwsDoc::S3::upload_mutex; // A condition_variable is a synchronization primitive that can be used to // block a thread, or to block multiple threads at the same time. // The thread is blocked until another thread both modifies a shared // variable (the condition) and notifies the condition_variable. std::condition_variable AwsDoc::S3::upload_variable;
void uploadFileAsyncFinished(const Aws::S3::S3Client *s3Client, const Aws::S3::Model::PutObjectRequest &request, const Aws::S3::Model::PutObjectOutcome &outcome, const std::shared_ptr<const Aws::Client::AsyncCallerContext> &context) { if (outcome.IsSuccess()) { std::cout << "Success: uploadFileAsyncFinished: Finished uploading '" << context->GetUUID() << "'." << std::endl; } else { std::cerr << "Error: uploadFileAsyncFinished: " << outcome.GetError().GetMessage() << std::endl; } // Unblock the thread that is waiting for this function to complete. AwsDoc::S3::upload_variable.notify_one(); }

Una vez finalizada la operación asíncrona, se pueden liberar los recursos asociados a ella. La aplicación también puede finalizar si lo desea.

El siguiente código muestra cómo utiliza una aplicación los métodos uploadFileAsync y uploadFileAsyncFinished.

La aplicación asigna los objetos S3Client y PutObjectRequest para que sigan existiendo hasta que finalice la operación asíncrona. Tras la llamadauploadFileAsync, la aplicación puede realizar las operaciones que desee. Por motivos de simplificación, en el ejemplo se utilizan std::mutex y std::condition_variable para esperar a que la devolución de llamada del controlador de respuestas notifique que la operación de carga ha finalizado.

int main(int argc, char* argv[]) { if (argc != 3) { std::cout << R"( Usage: run_put_object_async <file_name> <bucket_name> Where: file_name - The name of the file to upload. bucket_name - The name of the bucket to upload the object to. )" << std::endl; return 1; } const Aws::SDKOptions options; Aws::InitAPI(options); { const Aws::String fileName = argv[1]; const Aws::String bucketName = argv[2]; // A unique_lock is a general-purpose mutex ownership wrapper allowing // deferred locking, time-constrained attempts at locking, recursive // locking, transfer of lock ownership, and use with // condition variables. std::unique_lock<std::mutex> lock(AwsDoc::S3::upload_mutex); // Create and configure the Amazon S3 client. // This client must be declared here, as this client must exist // until the put object operation finishes. const Aws::S3::S3ClientConfiguration config; // Optional: Set to the AWS Region in which the bucket was created (overrides config file). // config.region = "us-east-1"; const Aws::S3::S3Client s3Client(config); // Create the request object. // This request object must be declared here, because the object must exist // until the put object operation finishes. Aws::S3::Model::PutObjectRequest request; AwsDoc::S3::uploadFileAsync(s3Client, request, bucketName, fileName); std::cout << "main: Waiting for file upload attempt..." << std::endl << std::endl; // While the put object operation attempt is in progress, // you can perform other tasks. // This example simply blocks until the put object operation // attempt finishes. AwsDoc::S3::upload_variable.wait(lock); std::cout << std::endl << "main: File upload attempt completed." << std::endl; } Aws::ShutdownAPI(options); return 0; }

Consulte el ejemplo completo en Github.