Non sono sicuro del sistema di ping DCOM, ma un'opzione per voi sarebbe semplicemente la generazione delle notifiche da un pool di thread separato. Ciò contribuirà a mitigare l'effetto di avere un piccolo numero di client di blocco: inizierai ad avere problemi quando ce ne sono troppi, naturalmente.
Il modo semplice per farlo è utilizzare QueueUserWorkItem
- questo richiamerà la richiamata passata sul pool di thread di sistema dell'applicazione. Supponendo che si sta utilizzando un MTA, questo è tutto quello che dovete fare:
static InfoStruct {
IRemoteHost *pRemote;
BSTR someData;
};
static DWORD WINAPI InvokeClientAsync(LPVOID lpInfo) {
CoInitializeEx(COINIT_MULTITHREADED);
InfoStruct *is = (InfoStruct *)lpInfo;
is->pRemote->notify(someData);
is->pRemote->Release();
SysFreeString(is->someData);
delete is;
CoUninitialize();
return 0;
}
void InvokeClient(IRemoteHost *pRemote, BSTR someData) {
InfoStruct *is = new InfoStruct;
is->pRemote = pRemote;
pRemote->AddRef();
is->someData = SysAllocString(someData);
QueueUserWorkItem(InvokeClientAsync, (LPVOID)is, WT_EXECUTELONGFUNCTION);
}
Se il thread principale è in una STA, questo è un po 'più complessa solo; basta usare CoMarshalInterThreadInterfaceInStream
e CoGetInterfaceAndReleaseStream
per passare il puntatore di interfaccia tra appartamenti:
static InfoStruct {
IStream *pMarshalledRemote;
BSTR someData;
};
static DWORD WINAPI InvokeClientAsync(LPVOID lpInfo) {
CoInitializeEx(COINIT_MULTITHREADED); // can be STA as well
InfoStruct *is = (InfoStruct *)lpInfo;
IRemoteHost *pRemote;
CoGetInterfaceAndReleaseStream(is->pMarshalledRemote, __uuidof(IRemoteHost), (LPVOID *)&pRemote);
pRemote->notify(someData);
pRemote->Release();
SysFreeString(is->someData);
delete is;
CoUninitialize();
return 0;
}
void InvokeClient(IRemoteHost *pRemote, BSTR someData) {
InfoStruct *is = new InfoStruct;
CoMarshalInterThreadInterfaceInStream(__uuidof(IRemoteHost), pRemote, &is->pMarshalledRemote);
is->someData = SysAllocString(someData);
QueueUserWorkItem(InvokeClientAsync, (LPVOID)is, WT_EXECUTELONGFUNCTION);
}
Si noti che il controllo degli errori è stato eliso per chiarezza - si, naturalmente, vuole errore di controllo tutte le chiamate - in particolare, si vuole sta controllando per RPC_S_SERVER_UNAVAILABLE
e altri tali errori di rete e rimuovere i client incriminati.
Alcune varianti più sofisticate che si possono prendere in considerazione includono l'assicurazione che solo una richiesta è in volo per client alla volta (riducendo ulteriormente l'impatto di un client bloccato) e memorizzando nella cache il puntatore dell'interfaccia marshalled nel MTA (se il proprio thread principale è uno STA) - poiché credo che CoMarshalInterThreadInterfaceInStream
possa eseguire richieste di rete, sarebbe preferibile occuparsene prima del momento in cui è noto al client, anziché rischiare di bloccare il thread principale.
Avete considerato di inviare gli eventi dai thread del thread thread, per mitigare il blocco in misura maggiore? – bdonlan
@bdonlan: Questa potrebbe essere una soluzione, tuttavia questo complicherà significativamente il server - dovrà prendersi cura di questi thread extra di durata. – sharptooth
Non proprio - puoi semplicemente usare il pool di thread win32 integrato. Se stai già utilizzando un MTA, è piuttosto semplice colpire QueueUserWorkItem. Se si è in uno STA, è necessario eseguire il marshalling dell'interfaccia remota nel MTA, ma non è ancora troppo difficile da utilizzare con CoMarshalInterThreadInterfaceInStream ecc. – bdonlan