Multithreading: uso de las clases de sincronización de MFCMultithreading: How to Use the MFC Synchronization Classes

Sincronizar el acceso a los recursos entre subprocesos es un problema habitual cuando se escriben aplicaciones multiproceso.Synchronizing resource access between threads is a common problem when writing multithreaded applications. Si dos o más subprocesos tienen acceso a los mismos datos simultáneamente, se pueden producir resultados impredecibles y no deseados.Having two or more threads simultaneously access the same data can lead to undesirable and unpredictable results. Por ejemplo, un subproceso podría estar actualizando el contenido de una estructura mientras que otro está leyendo el contenido de la misma estructura.For example, one thread could be updating the contents of a structure while another thread is reading the contents of the same structure. No se puede saber qué datos recibirá el proceso de lectura: los datos antiguos, los datos recién escritos o, posiblemente, una mezcla de ambos.It is unknown what data the reading thread will receive: the old data, the newly written data, or possibly a mixture of both. MFC proporciona una serie de clases de sincronización y de sincronización de acceso para contribuir a resolver este problema.MFC provides a number of synchronization and synchronization access classes to aid in solving this problem. Este tema describe las clases disponibles y su uso con el fin de crear clases seguras para subprocesos en una aplicación multiproceso típica.This topic explains the classes available and how to use them to create thread-safe classes in a typical multithreaded application.

Una aplicación multiproceso múltiple típica dispone de una clase que representa un recurso que se va a compartir entre subprocesos.A typical multithreaded application has a class that represents a resource to be shared among threads. Una clase bien diseñada completamente segura para la ejecución de subprocesos no requiere que el programador llame a ninguna función de sincronización.A properly designed, fully thread-safe class does not require you to call any synchronization functions. Todo se controla internamente en la clase; por tanto, el programador se puede concentrar en la mejor forma de usar la clase, no en si ésta puede resultar dañada por algún error.Everything is handled internally to the class, allowing you to concentrate on how to best use the class, not about how it might get corrupted. Una técnica eficaz para crear una clase segura para la ejecución de subprocesos consiste en integrar la clase de sincronización en la clase del recurso.An effective technique for creating a fully thread-safe class is to merge the synchronization class into the resource class. La integración de clases de sincronización en la clase compartida es un proceso sencillo.Merging the synchronization classes into the shared class is a straightforward process.

Como ejemplo, considere una aplicación que mantiene una lista vinculada de cuentas.As an example, take an application that maintains a linked list of accounts. Esta aplicación permite examinar hasta tres cuentas en ventanas independientes, pero sólo se puede actualizar una en un momento dado.This application allows up to three accounts to be examined in separate windows, but only one can be updated at any particular time. Cuando se actualiza una cuenta, los datos actualizados se envían a un archivo recopilatorio de datos.When an account is updated, the updated data is sent over the network to a data archive.

Esta aplicación de ejemplo utiliza los tres tipos de clases de sincronización.This example application uses all three types of synchronization classes. Dado que permite examinar hasta tres cuentas al mismo tiempo, utiliza el CSemaphore para limitar el acceso a tres objetos de vista.Because it allows up to three accounts to be examined at one time, it uses CSemaphore to limit access to three view objects. Cuando se produce un intento de ver una cuarta cuenta, la aplicación espera a que se cierre una de las tres primeras ventanas o bien genera un error.When an attempt to view a fourth account occurs, the application either waits until one of the first three windows closes or it fails. Cuando se actualiza una cuenta, la aplicación usa CCriticalSection para asegurarse de que solo se actualiza una cuenta a la vez.When an account is updated, the application uses CCriticalSection to ensure that only one account is updated at a time. Una vez que la actualización se realiza correctamente, señala CEvent, que libera un subproceso en espera de que se señale el evento.After the update succeeds, it signals CEvent, which releases a thread waiting for the event to be signaled. Este subproceso envía los nuevos datos al archivo de almacenamiento de datos.This thread sends the new data to the data archive.

Diseñar una clase Thread-SafeDesigning a Thread-Safe Class

Para hacer que una clase sea completamente segura para la ejecución de subprocesos, primero agregue la clase de sincronización apropiada a las clases compartidas como un miembro de datos.To make a class fully thread-safe, first add the appropriate synchronization class to the shared classes as a data member. En el ejemplo anterior de administración de cuentas, CSemaphore se agregaría un miembro de datos a la clase de vista, un CCriticalSection miembro de datos se agregaría a la clase de lista vinculada y CEvent se agregaría un miembro de datos a la clase de almacenamiento de datos.In the previous account-management example, a CSemaphore data member would be added to the view class, a CCriticalSection data member would be added to the linked-list class, and a CEvent data member would be added to the data storage class.

A continuación, agregue llamadas de sincronización a todas las funciones miembro que modifican los datos de la clase o que tienen acceso a un recurso controlado.Next, add synchronization calls to all member functions that modify the data in the class or access a controlled resource. En cada función, debe crear un objeto CSingleLock o CMultiLock y llamar a la función de ese objeto Lock .In each function, you should create either a CSingleLock or CMultiLock object and call that object's Lock function. Cuando el objeto de bloqueo queda fuera de ámbito y se destruye, el destructor del objeto llama automáticamente a Unlock y libera el recurso.When the lock object goes out of scope and is destroyed, the object's destructor calls Unlock for you, releasing the resource. No obstante, puede realizar una llamada a Unlock directamente si lo desea.Of course, you can call Unlock directly if you want.

El diseño de una clase segura para subprocesos realizado de esta forma permite utilizarla en aplicaciones multiproceso con la misma facilidad que una clase no diseñada para subprocesos, pero con un nivel muy alto de seguridad.Designing your thread-safe class in this fashion allows it to be used in a multithreaded application as easily as a non-thread-safe class, but with a higher level of safety. La encapsulación del objeto de sincronización y del objeto de sincronización de acceso en la clase del recurso proporciona todas las ventajas de la programación segura para subprocesos sin los inconvenientes de mantener el código encargado de la sincronización.Encapsulating the synchronization object and synchronization access object into the resource's class provides all the benefits of fully thread-safe programming without the drawback of maintaining synchronization code.

El siguiente ejemplo de código ilustra este método mediante el uso de un miembro de datos, m_CritSection (de tipo CCriticalSection), declarado en la clase del recurso compartido, y un objeto CSingleLock.The following code example demonstrates this method by using a data member, m_CritSection (of type CCriticalSection), declared in the shared resource class and a CSingleLock object. La sincronización del recurso compartido (derivado de CWinThread) se lleva a cabo mediante la creación de un objeto CSingleLock que utiliza la dirección del objeto m_CritSection.The synchronization of the shared resource (derived from CWinThread) is attempted by creating a CSingleLock object using the address of the m_CritSection object. Se realiza un intento de bloquear el recurso y, una vez conseguido, se realiza la tarea sobre el objeto compartido.An attempt is made to lock the resource and, when obtained, work is done on the shared object. Tras finalizar la tarea, el recurso se desbloquea mediante una llamada a Unlock.When the work is finished, the resource is unlocked with a call to Unlock.

CSingleLock singleLock(&m_CritSection);
singleLock.Lock();
// resource locked
//.usage of shared resource...

singleLock.Unlock();

Nota

CCriticalSection, a diferencia de otras clases de sincronización de MFC, no dispone de la opción de solicitud de bloqueo temporizado.CCriticalSection, unlike other MFC synchronization classes, does not have the option of a timed lock request. El período de espera para que un subproceso se libere es infinito.The waiting period for a thread to become free is infinite.

El inconveniente de este enfoque es que la clase será ligeramente más lenta que la misma clase sin los objetos de sincronización agregados.The drawbacks to this approach are that the class will be slightly slower than the same class without the synchronization objects added. Además, si existe la posibilidad de que varios subprocesos puedan eliminar el objeto, el enfoque de integración no siempre funcionará.Also, if there is a chance that more than one thread might delete the object, the merged approach might not always work. En esta situación, es mejor mantener objetos de sincronización independientes.In this situation, it is better to maintain separate synchronization objects.

Para obtener información sobre cómo determinar la clase de sincronización que se va a usar en situaciones diferentes, vea multithreading: Cuándo utilizar las clases de sincronización.For information about determining which synchronization class to use in different situations, see Multithreading: When to Use the Synchronization Classes. Para obtener más información acerca de la sincronización, consulte Synchronization in the Windows SDK.For more information about synchronization, see Synchronization in the Windows SDK. Para obtener más información sobre la compatibilidad con multithreading en MFC, vea multithreading con C++ y MFC.For more information about multithreading support in MFC, see Multithreading with C++ and MFC.

Consulta tambiénSee also

Subprocesamiento múltiple con C++ y MFCMultithreading with C++ and MFC