Newsgroup: it.comp.programmare.win32
Titolo: Come passare un oggetto al worker thread?

Ciao, sto cercando di convertire un'applicazione MFC monothread in
un'applicazione multithread...
Ho bisogno di un semplice Worker Thread, pe cui ho scritto la funzione
UINT ComputeThreadProc(LPVOID pParam);
a questo punto ho sostituito la chiamata alla procedura di calcolo in questo
modo:
all'interno della mia classe CView...
AfxBeginThread(ComputeThreadProc, (LPVOID)GetSafeHwnd());
la Thread Procedure e' questa:
UINT ComputeThreadProc(LPVOID pParam);
{
	CBirisOldView *pV = (CBirisOldView *) CWnd::FromHandlePermanent((HWND)
pParam);
	if (pV != NULL)
	{
		ComputeRotation(pV->pDoc);
		pV->Stop();
	}
	else
		AfxMessageBox("NULL");
	return 0;
}
Il problema e' che dopo la chiamata a CWnd::FromHandlePermanent((HWND)
pParam), pV e' NULL e non riesco a combinare nulla... :.-(
Ho provato a chiamare la Thread Procedure in questo modo:
AfxBeginThread(ComputeThreadProc, (LPVOID)m_hWnd);
ma nulla, cosi' come se uso
CWnd::FromHandle invece di CWnd::FromHandlePermanent
non cambia nulla...
se passo this, in questo modo:
AfxBeginThread(ComputeThreadProc, (LPVOID)this);
quando accedo all'oggetto view ottengo una assert che consiglia di passare
gli Handle e non gli oggetti tra i vari threads...
Qualche idea??
Conoscete qualche sito dove posso trovare esempi a riguardo?
Ho spulciato Codeguru e CodeProject ma non ho trovato nulla.
Ho anche provato a costruire un UI Thread ma non ho risolto il problema e
comunque, dovendo svolgere semplicemente dei calcoli, so di aver bisogno di
un Worker Thread...
Grazie mille, ciao e buon lavoro, Davide.
-- 
Posted from 
origin.vit.iit.nrc.ca
 [132.246.41.10] 
via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 gli Handle e non gli oggetti tra i vari threads...
Mi pare molto strano, io faccio sempre cosi` e non mi compare nessuna
assert. A che tipo di oggetto punta this?
---
Bye! Lorenzo Lutti
http://antani.virtualave.net/

 pV->Stop();
Se questa funzione provoca l'ASSERT, e` qualcosa fatta al suo interno che lo
provoca, non tanto il fatto di averla chiamata...


Ciao Lorenzo.
this punta ad un discendente della classe CView (CBirisOldView nel codice),
ho notato che se passo this alla Thread Procedure, quando poi lo vado a
castare ed usare, PUR ESSENDO VALIDO, mi compare la Assert...
La ASSERT sollevata e' questa (file WINCORE.cpp):
	else
	{
	CObject* p;
		ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||
			(p = pMap->LookupTemporary(m_hWnd)) != NULL);
		ASSERT((CWnd*)p == this);   // must be us
		// Note: if either of the above asserts fire and you are
		// writing a multithreaded application, it is likely that
		// you have passed a C++ object from one thread to another
		// and have used that object in a way that was not intended.
		// (only simple inline wrapper functions should be used)
		//
		// In general, CWnd objects should be passed by HWND from
		// one thread to another.  The receiving thread can wrap
		// the HWND with a CWnd object by using CWnd::FromHandle.
		//
		// It is dangerous to pass C++ objects from one thread to
		// another, unless the objects are designed to be used in
		// such a manner.
	}
la mia Thread Procedure e' questa:
UINT ComputeThreadProc(LPVOID pParam)
{
	CBirisOldView *pV = (CBirisOldView *) pParam;
	if (pV != NULL)
	{
		ComputeRotation(pV->pDoc);
		pV->Stop();
	}
	else
		AfxMessageBox("NULL");
	return 0;
}
il bello e' che eseguendo il DEBUG passo-passo, la chiamata a
ComputeRotation(pV->pDoc) non crea problemi, ma la successiva chiamata a
pV->Stop() solleva la ASSERT precedente!!! Come dire che se passo il
puntatore ad un oggetto, lo posso poi usare solo la prima volta!
Magari se faccio una PostMessage invece di invocare direttamente il metodo
stop cambia qualcosa, ma vorrei capire bene dove sbaglio visto che nei
prossimi mesi usero' intensamente il multithreading...
Grazie per l'attenzione, ciao e buon lavoro, Davide.


 ha scritto nel messaggio
news:BHEKJEKAPCNFBMCMACAF...@nrc.ca...

Io credo che tu ti stia scontrando con la barriera dei thread.
Passare oggetti da un thread ad un altro può essere pericoloso e così in MFC
è realizzata una sorta di barriera.
Tutte le finestre MFC create all'interno di un thread non sono visibile
dall'altro.
Cosa vuol dire questo ?
Quano il thread 'A' crea un'oggetto finestra (e derivati), nello spazio del
thread viene aggiornata una mappa che collega un puntatore al CWnd* della
finestra appena creata al proprio Handle HWND.
Questa mappa registra quindi tutti gli oggetti creati dal thread.
Ora con l'istruzione CWnd::FromHandlePermanent viene esaminata la mappa del
thread e viene restituito il puntatore all'oggetto originale che è stato
creato nel thread.
Chiarisco ulteriormente:
supponiamo che io creo una finestra m_pippownd di classe CPippoWnd (derivata
da CWnd) all'interno del thread A e questa finestra abbia handle pippohwnd.
Esiste quindi una tabella gestita dal thread che associa &m_pippownd con
pippohwnd.
Se io effettuo una chiamata a CWnd::FromHandlePermanent(pippohwnd) il thread
esamina la propria tabella per vedere se c'è l'handle.
Se lo trova la funzione restituisce un CWnd* a m_pippownd. Poiché è il
puntatore all'oggetto originale il cast alla classe specializzata CPippoWnd*
è lecito.
Se io passo al thread B l'handle pippohwnd della finestra m_pippownd e
chiamo dal thread B la funzione FromHandlePermanent, questa mi ritorna NULL,
perché ?
Perché adesso sono per l'appunto nel thread B e la funzione
FromHandlePermanent esamina la tabella del thread B in cerca di un oggetto
collegato all'handle pippohwnd. Ovviamente non essendo presente in quella
tabella la funzione ritorna NULL.
Chiaro fin qui ? Speriamo.....
Potrei usare FromHandle ? Si potresti ma in questo caso non ti serve.
FromHandle fa la stessa cosa di FromHandlePermanent tranne che se non può
trovare l'oggetto collegato all'handle crea una classe CWnd al volo
(temporanea) e te ne restituisce il puntatore.
Soltanto che non è l'oggetto originale m_pippownd bensì un puntatore ad un
oggetto CWnd creato al volo, percui puoi chiamare tutti i metodi della
classe CWnd ma il
cast a CPippoWnd* è illecito.
Questa considerazione spiega perché il tentativo di passare l'handle della
tua finestra al worker thread e poi chiamare le funzioni FromHandle e
FromHandlePermanent abbia fallito.
Potresti dunque passare il puntatore al tuo oggetto direttamente al worker
thread, in questa maniera:
AfxBeginThread(ComputeThreadProc, (LPVOID)this);
E tu mi dirai: "ma come !!!! c'ho provato e non funziona".
Certo, può funzionare e può non funzionare ! Questo dipende dall'oggetto:
la funzione ComputeRotation(pV->pDoc) come hai detto tu funziona.
Questa funzione evidentemente accede solo agli elementi dell'ogetto
CBirisOldView e non fa alcun controllo.
La funzione pV->Stop() invece dà errore, evidentemente da qualche parte
viene chiamata la funzione AssertValid dell'oggetto pV (o di un qualche
oggetto collegato)
che si accorge che l'oggetto non è del thread B ma è del thread A e si
arrabbia.
Visto che AssertValid è virtuale potresti modificarne il comportamento per
togliere l'errore, ma devi essere sicuro di quello che fai.
La soluzione più "pulita" penso sia proprio di rivolgersi alle SendMessage o
PostMessage,
che thread-safe.
Ho finito.
Ciao
Porlock