Pregunta

Si tengo un IUnknown *ptr, necesito llamar a Release() en cada interfaz de obtener a través de ptr->QueryInterface(), además para llamar a ptr->Release() cuando he terminado con el ptr?

Yo solía pensar que la respuesta es "Sí", pero esta cita de MSDN me confunde:

Ocasionalmente, puede que tenga que obtener una débil referencia a un objeto (es decir, usted puede obtener un puntero a una de sus interfaces sin incrementar el número de referencia), pero no es aceptable que para hacer esta llamando QueryInterface seguido por Release.

No entiendo por que la problemática -- si me llaman ptr->QueryInterface() y, a continuación, llamar a Release en el puntero, no el recuento de referencia en el objeto todavía de ser positivo?¿Cómo es que el resultado en un puntero no válido?

¿Fue útil?

Solución

La documentación es correcta. Y debe seguir reglas de conteo de referencia, que incluyen llamar Release en interfaces obtenidas de QueryInterface además de después de crear el objeto.

Para aclarar por qué no puedes hacer consejos débiles con Release - Existe una condición de carrera al llamar QueryInterface y entonces Release inmediatamente despues.

  • Thread1 crea objeto - Recuento de referencia 1
  • Llamadas de Thread2 QueryInterface Para referencia débil - Recuento de referencia 2
  • Objeto de liberación de Thread1 - Conteo de referencia 1
  • Llamadas de Thread2 Release Para referencia débil - Recuento de referencia 0. El objeto se destruye.
  • Thread2 intenta usar objeto - error.

La advertencia está ahí para protegerse contra lo anterior; presumiblemente, algunos programadores piensan que pueden "llamar ptr->QueryInterface() y luego llamar Release en el puntero resultante "y luego usa el objeto ...

Otros consejos

Iunknown :: Método QueryInterface

Recupera punteros a las interfaces compatibles en un objeto.

Este método llama a iunknown :: addref en el puntero que devuelve.

Directamente de iunknown :: QueryInterface Reference enhttp://msdn.microsoft.com/en-us/library/ms682521%28v=vs.85%29.aspx

Theading no es el único escenario; Yo iría tan lejos como para decir que el enhebrado no es en realidad el escenario principal en absoluto: estas reglas de COM se remontan a Win16 antes de que se agregara a Windows en primer lugar.

La cuestión clave es que en lo que respecta a COM, los recuentos de referencia soninterfaz, no per-objeto. Una implementación de COM es gratuita para implementar un recuento de referencias implementándolo por objeto, esa es quizás la forma más simple de hacerlo en C ++, especialmente cuando un objeto COM se asigna a un solo objeto C ++, pero eso no es más que un detalle de implementación, y el código del cliente COM no puede confiar en que sea el caso.

Hay muchos objetos COM que pueden generar interfaces en la mosca según sea necesario, y luego destruirlos tan pronto como ya no sean necesarios. En esos casos, si llama a Qi para obtener una de estas interfaces, una vez que llama a la versión, la memoria para esa interfaz puede ser orientada, por lo que usarla podría conducir a una falla/bloqueo/etc.

En términos generales, debe considerar cualquier llamada a -> Release () como potencialmente repartir la memoria detrás del puntero.

(Además, tenga en cuenta que COM realmente no tiene un concepto de referencias débiles para comenzar: hay referencias (fuertes) contadas, y eso es todo).

La sugerencia para evitar referencias débiles no resuelve el problema de la carrera.

T1 operator new, create object, references: 1
T1     passes interface object reference to T2, thinking it can "share" ownership
T1     suspends
T2     resumes
T2 QueryInterface
T2     suspends before InterlockedIncrement, references: 1
T1     resumes
T1 Calls Release
T1     suspends between InterlockedDecrement and operator delete, references: 0
T2     resumes, InterlockedIncrement occurs, references 1
T2     suspends
T1     resumes, operator delete executes, references 1 !!!
T1     suspends
T2     resumes
T2 Any reference to the interface is now invalid since it has been deleted with reference count 1.

Esto se puede solucionar en el servidor COM. Sin embargo, el cliente COM no debe depender del servidor que evite esta condición de carrera. Por lo tanto, los clientes COM no deben compartir objetos de interfaz entre hilos. El único hilo que debe permitirse acceder al objeto de interfaz es el único hilo que actualmente "posee" el objeto de interfaz.

T1 no debería haber llamado liberación. Sí, podría haber llamado a Addref antes de pasar el objeto de interfaz a T2. Pero eso puede no resolver la carrera, solo muévala en otro lugar. La mejor práctica es mantener siempre el concepto de un objeto de una interfaz, un solo propietario.

Si el servidor COM desea admitir el concepto de que dos (o más) interfaces pueden hacer referencia a algún estado interno de servidor compartido, el servidor COM debe anunciar el contrato suministrando un método CreateCopyOfInstance y administrar la contención, internamente. Hay, por supuesto, ejemplos de servidores que manejan este tipo de "ventilador". Eche un vistazo a las interfaces de almacenamiento persistentes de Microsoft. Allí, las interfaces no "se" abanzan ". Cada interfaz debe ser propiedad de un solo usuario (hilo/proceso/lo que sea) y el" fan-out "se administra internamente, con los métodos proporcionados a la COM Clientes para controlar algunas facetas de los problemas de contención. Los servidores COM de Microsoft deben, por lo tanto, abordar las condiciones de carrera como parte de sus contratos a sus clientes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top