J'ai déjà beaucoup travaillé sur le multithreading, mais je suis relativement nouveau dans COM. De toute façon, voici mon problème:Synchronisation en toute sécurité d'un thread COM
Je crée un thread de travail, qui s'enregistre en tant que STA et crée un objet COM. Ensuite, le thread de travail et le thread principal essaient de communiquer entre eux. En utilisant CoMarshalInterThreadInterfaceInStream
et CoGetInterfaceAndReleaseStream
, je peux obtenir les threads pour appeler des méthodes sur les objets COM dans l'autre thread.
Voici ce que le thread de travail ressemble à:
void workerThread()
{
CoInitialize(NULL);
MyLib::IFooPtr foo = ...; // create my COM object
// Marshall it so the main thread can talk to it
HRESULT hr = CoMarshalInterThreadInterfaceInStream(foo.GetIID(),
foo.GetInterfacePtr(),
&m_stream);
if (FAILED(hr)) {
// handle failure
}
// begin message loop, to keep this STA alive
MSG msg;
BOOL bRet;
while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1) break;
DispatchMessage(&msg);
}
}
Dans le thread principal:
// launch the thread
m_worker = boost::thread (&workerThread);
// get the interface proxy
MyLib::IFooPtr foo;
LPVOID vp (NULL);
HRESULT hr = CoGetInterfaceAndReleaseStream(m_stream, foo.GetIID(), &vp);
if (SUCCEEDED(hr)) foo.Attach(static_cast<MyLib::IFoo*>(vp));
Cela crée l'objet (qui prend un certain temps pour initialiser) et permet le fil principal Parlez-en, et tout est correctement synchronisé avec les trucs COM Apartment. Autant que je sache en lisant msdn, cela semble être la bonne façon de faire les choses. Maintenant, le thread principal peut utiliser son proxy pour appeler des méthodes sur mon objet COM, et le thread de travail recevra ces appels sur la file d'attente de messages, en les envoyant correctement.
Cependant, qu'en est-il de la synchronisation de ces threads?
Il est évident que dans ce cas, je veux que le fil conducteur d'attendre pour appeler CoGetInterfaceAndReleaseStream
jusqu'à ce que le thread de travail a créé ce flux via CoMarshalInterThreadInterfaceInStream
. Mais comment puis-je faire cela en toute sécurité?
De MSDN, je devrais utiliser quelque chose comme MsgWaitForMultipleObjects
, donc je peux attendre my_condition OU new_message_arrived, et je peux faire quelque chose comme:
// verbatim from msdn
while (TRUE)
{
// wait for the event and for messages
DWORD dwReturn = ::MsgWaitForMultipleObjects(1,
&m_hDoneLoading, FALSE, INFINITE, QS_ALLINPUT);
// this thread has been reawakened. Determine why
// and handle appropriately.
if (dwReturn == WAIT_OBJECT_0)
// our event happened.
break ;
else if (dwReturn == WAIT_OBJECT_0 + 1)
{
// handle windows messages to maintain
// client liveness
MSG msg ;
while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
::DispatchMessage(&msg) ;
}
}
Mais comment puis-je mélanger boost::thread.join()
et boost::condition.wait()
avec MsgWaitForMultipleObjects
? Est-ce possible, ou dois-je faire autre chose pour éviter une condition de course?
non, je ne suis pas sûr que ce soit ce que je veux. J'ai affaire à une application monothread existante, essayant d'améliorer le temps de démarrage en ajoutant des threads. Je me rends compte que je négocie le temps de démarrage pour la durée de fonctionnement générale. J'espère finir par arriver à MTA, mais utiliser STA comme une étape intermédiaire dans le développement. – Tim
Si vous avez lancé N threads, chacun créant un objet STA, chaque thread, dès qu'il a créé et assemblé l'interface dans le flux, décrémente un compteur (InterlockedDecrement). Celui qui le décrémente à 0 est le dernier, et peut signaler un événement. Le thread principal reste inactif en attente d'un événement, et quand il est signalé, il peut continuer en toute sécurité et ne pas écouter tous les flux. Cela a un impact minimal sur la logique de l'application héritée tout en permettant de créer des objets N COM en parallèle. –
Pouvez-vous donner un exemple de cela? Je ne comprends pas comment utiliser 'PostThreadMessage' et' MsgWaitForMultipleObjects'. – Tim