PDA

View Full Version : VC++ e MFC: Sono alle prime armi!


Fabrizio73
27-04-2002, 19:10
Di (V)C++ e MFC so pochissimo, praticamente nulla e vorrei porre rimedio a qs mia lacuna.

Ho tentanto di realizzare un programmino che crea un thread, il quale si limita a visualizzare un parametro passatogli all'atto della creazione.

#include <iostream.h>
#include <afxwin.h>


UINT threadBody (LPVOID pParam) {
int* threadParam = (int*) pParam;
cout << *threadParam << endl;
return 0;
}


void main (int argc, char* argv[]) {
int threadParam = 0;
CWinThread* thread = AfxBeginThread (threadBody, &threadParam);
}

Tuttavia durante il linking ottengo qs errori:

--------------------Configuration: prova - Win32 Debug--------------------
Compiling...
prova.cpp
Linking...
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
Debug/prova.exe : fatal error LNK1120: 2 unresolved externals
Error executing link.exe.

prova.exe - 3 error(s), 0 warning(s)

Dov'è il problema?

JAVA dove sei :( ?

cionci
27-04-2002, 19:53
Hai linkato il file senza MFC... Project -> Settings -> General e nel menù a tendina seleziona MFC come shared DLL...

Fabrizio73
27-04-2002, 20:11
Thanks ;)... però il programma non funziona, infatti dovrebbe visualizzare il contenuto di threadParam, ossia 0 ma non visualizza nulla... che altro ho combinato?

-=Krynn=-
28-04-2002, 01:53
uh, anche io mi sto scontrando contro quel muro che è la MFC.....
se riesci a risolvere il tuo problema dimelo magari in pvt

cionci
28-04-2002, 13:02
Originariamente inviato da Fabrizio73
[B]Thanks ;)... però il programma non funziona, infatti dovrebbe visualizzare il contenuto di threadParam, ossia 0 ma non visualizza nulla... che altro ho combinato?
Il problema sta nel fatto che il thread che lanci finisce dopo il main...quindi la variabile threadParam non risulta più inizializzata...

Quindi le soluzioni possono essere diverse...

Ad esempio metti una Sleep in fondo al main...oppure la soluzione corretta è usare la WaitForSingleObject...

Il problema è che non so come recuperare l'HANDLE dal CWinThread...ho usato la WaitForSingleObject per molte cose, ma non per i thread...

Una soluzione potrebbe essere creare il thread con
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security attributes
DWORD dwStackSize, // initial thread stack size
LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
LPVOID lpParameter, // argument for new thread
DWORD dwCreationFlags, // creation flags
LPDWORD lpThreadId // pointer to receive thread ID
);

che come vedi ritorna l'HANDLE da poter usare con la WaitForSingleObject...

Fabrizio73
28-04-2002, 14:40
Che SVISTA... hai ragione il main thread termina prima!!!

Ho risolto così:

#include <iostream.h>
#include <afxwin.h>


UINT threadBody (LPVOID pParam) {
int* threadParam = (int*) pParam;
cout << *threadParam << endl;
return 0;
}


void main (int argc, char* argv[]) {
int threadParam = 0;
CWinThread* thread = AfxBeginThread (threadBody, &threadParam);
[B]HANDLE threadHandle = (*thread).m_hThread;
::WaitForSingleObject (threadHandle, INFINITE);[/B]
}

Grazie 1000 ;)

Cionci for Moderator :D

Fabrizio73
28-04-2002, 15:01
Mi sta venendo un dubbio... se il thread creato invece, terminasse prima che il main thread riuscisse a recuperare l'handle... che accadrebbe?

Forse sarebbe il caso di creare il thread in modalità suspended, recuperare l'handle (o duplicare l'handle?) e quindi effettuare il resume!

E' giusto?

Fabrizio73
28-04-2002, 15:10
#include <iostream.h>
#include <afxwin.h>


UINT threadBody (LPVOID pParam) {
int* threadParam = (int*) pParam;
cout << *threadParam << endl;
return 0;
}


void main (int argc, char* argv[]) {
int threadParam = 0;
CWinThread* thread = AfxBeginThread (threadBody, &threadParam,
THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
HANDLE threadHandle;
::DuplicateHandle (GetCurrentProcess (), (*thread).m_hThread,
GetCurrentProcess (), &threadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
(*thread).ResumeThread ();
::WaitForSingleObject (threadHandle, INFINITE);
::CloseHandle (threadHandle);
}

Possibile che si debba fare tutto questo casino?
Java diventa sempre più il mio preferito...

Fabrizio73
28-04-2002, 15:43
Ho trovato una soluzione più bella :): impedire alla MFC di distruggere l'oggetto CWinThread quando esso termina, in questo modo si può recuperare l'handle a patto di distruggere "manualmente" l'oggetto...

#include <iostream.h>
#include <afxwin.h>


UINT threadBody (LPVOID pParam) {
int* threadParam = (int*) pParam;
cout << *threadParam << endl;
return 0;
}


void main (int argc, char* argv[]) {
int threadParam = 0;
CWinThread* thread = AfxBeginThread (threadBody, &threadParam,
THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
(*thread).m_bAutoDelete = FALSE;
HANDLE threadHandle = (*thread).m_hThread;
(*thread).ResumeThread ();
::WaitForSingleObject (threadHandle, INFINITE);
delete thread;
}

cionci
28-04-2002, 17:19
Vabbè...niente male...

cionci
28-04-2002, 17:25
Originariamente inviato da Fabrizio73
[B]Mi sta venendo un dubbio... se il thread creato invece, terminasse prima che il main thread riuscisse a recuperare l'handle... che accadrebbe?
Ma sinceramente credo che se fa un WaitForSingleObject su un HANDLE non più valido esca subito...

Fabrizio73
30-04-2002, 18:09
Scusatemi se vi annoio ancora :rolleyes:... ma ho un altro problemino :D.

Ho realizzato qs classe:

class Client
{
private:
double interArrivalMeanTime;
Queue* queue;
BOOL isInterrupted;
CWinThread* thread;
UINT threadBody (LPVOID pParam);
public:
Client (double interArrivalMeanTime, Queue* queue);
~Client ();
void start ();
void interrupt ();
};


UINT Client::threadBody (LPVOID pParam)
{
// Corpo del thread...
return 0;
}


Client::Client (double interArrivalMeanTime, Queue* queue)
{
(*this).interArrivalMeanTime = interArrivalMeanTime;
(*this).queue = queue;
isInterrupted = FALSE;
[COLOR=RED]thread = AfxBeginThread (threadBody, NULL, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);[/COLOR]
(*thread).m_bAutoDelete = FALSE;
}


Client::~Client ()
{
delete thread;
}


void Client::start ()
{
(*thread).ResumeThread ();
}


void Client::interrupt ()
{
isInterrupted = TRUE;
}

All'atto della compilazione, viene segnalato il seguente errore sulla linea di codice in rosso:

error C2665: 'AfxBeginThread' : none of the 2 overloads can convert parameter 1 from type 'unsigned int (void *)'

Se invece definisco la classe Client in qs modo, ossia faccio in modo che la funzione threadBody sia globale...

class Client
{
private:
double interArrivalMeanTime;
Queue* queue;
BOOL isInterrupted;
CWinThread* thread;
public:
Client (double interArrivalMeanTime, Queue* queue);
~Client ();
void start ();
void interrupt ();
};


[COLOR=BLUE]UINT threadBody (LPVOID pParam)
{
// Corpo del thread...
return 0;
}[/COLOR]


Client::Client (double interArrivalMeanTime, Queue* queue)
{
(*this).interArrivalMeanTime = interArrivalMeanTime;
(*this).queue = queue;
isInterrupted = FALSE;
thread = AfxBeginThread (threadBody, NULL, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
(*thread).m_bAutoDelete = FALSE;
}


Client::~Client ()
{
delete thread;
}


void Client::start ()
{
(*thread).ResumeThread ();
}


void Client::interrupt ()
{
isInterrupted = TRUE;
}

...non viene segnalato alcun errore di compilazione!

Cosa non va :confused:?

cionci
30-04-2002, 19:07
Prova a definire la funzione threadBody come static UINT ThreadBody(LPVOID pParam)...

cionci
30-04-2002, 19:19
Guarda un po'...questa sembra funzionare bene...

#include <iostream.h>
#include <afxwin.h>


typedef char Queue;

class Client
{
private:
double interArrivalMeanTime;
Queue* queue;
BOOL isInterrupted;
CWinThread* thread;
static UINT threadBody2 (LPVOID pParam);
public:
Client (double interArrivalMeanTime, Queue* queue);
~Client ();
void start ();
void interrupt ();
};


UINT Client::threadBody2 (LPVOID pParam)
{
// Corpo del thread...

cout << ((Client*)pParam)->interArrivalMeanTime << endl;
return 0;
}


Client::Client (double interArrivalMeanTime, Queue* queue)
{
(*this).interArrivalMeanTime = interArrivalMeanTime;
(*this).queue = queue;
isInterrupted = FALSE;
thread = AfxBeginThread (threadBody2, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
(*thread).m_bAutoDelete = FALSE;
}


Client::~Client ()
{
delete thread;
}


void Client::start ()
{
(*thread).ResumeThread ();
}


void Client::interrupt ()
{
isInterrupted = TRUE;
}

UINT threadBody (LPVOID pParam) {
int* threadParam = (int*) pParam;
cout << *threadParam << endl;
return 0;
}


void main (int argc, char* argv[]) {
int threadParam = 0;

Client *thread[5];

for(int i=0; i<5; ++i)
{
thread[i] = new Client((double)i,0);
thread[i]->start();
}
Sleep(300);
}

Fabrizio73
30-04-2002, 19:36
Bhé... che dire... sei un genio :D... ma xché è necessario dichiarare la funzione di controllo del thread static?

cionci
30-04-2002, 19:46
Forse perchè gli indirizzi delle funzioni vengono tradotti al tempo della compilazione quindi non essendo static (cioè c'è una sola istanza di quella funzione valida per tutte le classi) non può determinare a priori a quale istanza della classe ti riferisci...

Fabrizio73
30-04-2002, 19:52
Non fa una grinza :D!!!

Ascolta un po'... in Java esistono due comodissime funzioni, molto utili in problemi del tipo produttore/consumatore. Mi riferisco alle funzioni wait () e notify () (esiste anche notifyAll ()). Esiste qualcosa di simile in VC++/MFC?

cionci
30-04-2002, 20:05
Spiegami un po' il significato...anche se un po' mi sembra di capirlo...

Fabrizio73
30-04-2002, 20:22
Ti faccio un esempio. Supponiamo di voler simulare un sistema client/server assegnando ad un thread il compito del client e ad un altro il compito del server. Il client formula le richieste inserendole in una coda (queue). Il server non fa altro che estrarre le richieste dalla coda ed eseguirle.

Per evitare che il server esegua un'attesa attiva sulla coda (ossia controlli continuamente se vi sono richieste), esso può invocare il metodo wait dell'oggetto queue e si addormenta:

synchronized (queue) {
if (queue.isEmpty ()) {
queue.wait ();
}
request = (Request)queue.get ();
}

Quando il client inserisce una richiesta nella coda, provvede a risvegliere il thread server invocando il metodo notify dell'oggetto queue:

synchronized (queue) {
queue.put (request);
queue.notify ();
}

C'è da dire che in Java ogni classe possiede di default alcuni metodi fra i quali ci sono wait e notify, per cui non è necessario definire qs funzioni quando si definisce la classe.

Inoltre un thread per poter invocare la wait di un oggetto, deve possedere il lock su tale oggetto (synchronized). Quando la wait viene invocata il lock sull'oggetto viene automaticamente rilasciato per evitare deadlock.

Carino... no?

Tutto questo è possibile in VC++/MFC?

cionci
30-04-2002, 20:32
In pratica che nei sistemi oeprativi si chiama semaforo ?!?!?

CMultiLock does not have a base class.

An object of class CMultiLock represents the access-control mechanism used in controlling access to resources in a multithreaded program. To use the synchronization classes CSemaphore, CMutex, and CEvent, you can create either a CMultiLock or CSingleLock object to wait on and release the synchronization object. Use CMultiLock when there are multiple objects that you could use at a particular time. Use CSingleLock when you only need to wait on one object at a time.

To use a CMultiLock object, first create an array of the synchronization objects that you wish to wait on. Next, call the CMultiLock object’s constructor inside a member function in the controlled resource’s class. Then call the Lock member function to determine if a resource is available (signaled). If one is, continue with the remainder of the member function. If no resource is available, either wait for a specified amount of time for a resource to be released, or return failure. After use of a resource is complete, either call the Unlock function if the CMultiLock object is to be used again, or allow the CMultiLock object to be destroyed.

CMultiLock objects are most useful when a thread has a large number of CEvent objects it can respond to. Create an array containing all the CEvent pointers, and call Lock. This will cause the thread to wait until one of the events is signaled.

For more information on how to use CMultiLock objects, see the articleMultithreading: How to Use the Synchronization Classes in Visual C++ Programmer’s Guide.

Fabrizio73
30-04-2002, 21:27
No... non mi sono spiegato... non mi riferivo ai semafori e alle sezioni critiche...

Mi rifaccio all'esempio già fatto.

Il thread server esegue le richieste presenti nella coda (queue). Se nella coda non ci sono richieste il thread server può controllare in continuazione se nella coda siano state accodate richieste d'esecuzione (attesa attiva). In alternativa, può invocare il metodo wait dell'oggetto queue. L'invocazione di questo metodo fa si che lo scheduler dei threads della JVM addormenti il thread invocante.

Quando il thread client finalemente inserisce nella coda una richiesta d'esecuzione, esso provvede ad invocare il metodo notify dell'oggetto queue. L'invocazione di questo metodo fa si che lo scheduler della JVM risvegli il thread (il thread server) che aveva invocato la wait sull'oggetto queue.

Sono stato più chiaro?

Fabrizio73
01-05-2002, 09:52
Forse qs genere di problemi è risolto dalla classe CEvent...

cionci
01-05-2002, 10:10
Originariamente inviato da Fabrizio73
[B]No... non mi sono spiegato... non mi riferivo ai semafori e alle sezioni critiche...

Mi rifaccio all'esempio già fatto.

Il thread server esegue le richieste presenti nella coda (queue). Se nella coda non ci sono richieste il thread server può controllare in continuazione se nella coda siano state accodate richieste d'esecuzione (attesa attiva). In alternativa, può invocare il metodo wait dell'oggetto queue. L'invocazione di questo metodo fa si che lo scheduler dei threads della JVM addormenti il thread invocante.

Quando il thread client finalemente inserisce nella coda una richiesta d'esecuzione, esso provvede ad invocare il metodo notify dell'oggetto queue. L'invocazione di questo metodo fa si che lo scheduler della JVM risvegli il thread (il thread server) che aveva invocato la wait sull'oggetto queue.

Sono stato più chiaro?
Appunto è un semaforo...
Lo inizializzi al numero di elementi occupati della coda (se è vuota 0)...
Chi inserisce fa una signal...chi preleva fa una wait...
Se faccio una wait sul semaforo a zero mi blocco in attesa di venire risvegliato da una signal...
Ah...ovviamente ci vuole una mutex sull'accesso a questa sezione critica per i client...

Fabrizio73
01-05-2002, 10:27
Hai ragione... quello che mi serve è un semaforo... leggendo la documentazione della classe CSemaphore mi era sfuggito che il thread venisse effettivamente bloccato...

Un'altra domanda. Come hai detto giustamente, le operazioni di wait (CSemaphore.Lock ()) e signal (CSemaphore.Unlock ()) vanno inserite in sezioni critiche. Supponiamo che un thread A invochi una wait, prima di farlo deve detenere il lock sulla sezione critica. Supponiamo che l'invocazione della wait blocchi il thread A. In questo caso A non potrebbe rilasciare il lock sulla sezione critica, per cui il thread B che dovrebbe invocare la signal, non potrebbe farlo, in quanto resterebbe bloccato in attesa che A rilasci il lock della sezione critica. In qusto caso ci sarebbe un deadlock.

E' così?

cionci
01-05-2002, 10:34
Sì...infatti la sezione critica deve iniziare dopo il Lock sul semaforo...e terminare dopo l'Unlock sul semaforo (così ad occhio)...

cionci
01-05-2002, 10:37
Ho fatto un po' di confusione...
Ovviamente non serve l'unlock sul semaforo...

cionci
01-05-2002, 10:46
Ho fatto parecchia confusione...
Allora...per il server (m è la CMutex...s è il CSemaphore):

s.Lock(); //bloccante in caso di coda vuota
m.Lock();
Prelevo i dati
Decremento il contatore del buffer circolare
m.Unlock()

Per il client :

s.Unlock(); //bloccante in caso di coda piena
m.Lock();
Inserisco i dati
Incremento il contatore del buffer circolare
m.Unlock();

Così va bene ?

Fabrizio73
01-05-2002, 11:35
Perfect :D... almeno così sembra...

cionci
01-05-2002, 11:44
Sembra, ma non funziona...

Non so perchè...il semaforo non blocca gli ingressi in più !!!

Ti paso il file...

cionci
02-05-2002, 12:28
Bastava sfruttare un po' meglio le funzioni di sincronizzazione (il semaforo va loccato dentro alla mutex)...

Ah...quello che ho fatto lì usa uno stack (per prova di soli 2 elementi)...quindi estraggo l'utlimo entrato...
Non avevo voglia di farmi la coda circolare :)

Fabrizio73
02-05-2002, 13:10
Non ho visto bene il codice che hai allegato, tuttavia continuo a ritenere che il semaforo non vada bloccato dentro il mutex, in quanto se l'invocazione della lock sul semaforo dovesse bloccare il thread invocante, il mutex resterebbe "chiuso" e quindi l'eventuale altro thread che dovrebbe sbloccare il semaforo non potrebbe farlo (almeno che si eviti di inserire l'invocazione dell'unlock del semaforo nel mutex.)


ERRATO

// Thread A
mutex.lock ();
semaphore.lock ();
// ...
mutex.unlock ();


// Thread B
mutex.lock ();
// ...
semaphore.unlock ();
mutex.unlock ();


CORRETTO

// Thread A
semaphore.lock ();
mutex.lock ();
// ...
mutex.unlock ();


// Thread B
mutex.lock ();
// ...
mutex.unlock ();
semaphore.unlock ();


Nel frattempo sto facendo i miei esperimenti...

Tu cosa pensi?

cionci
02-05-2002, 13:53
Non puoi chiamare la unlock sul semaforo dopo aver aggiunto un nuovo elemento...nel caso di coda piena non funziona...

Per il client :

restart:
m.Lock();
if(s.Unlock(1))
{
aggiungo un nuovo elemento ed incremento i contatori
m.Unlock();
}
else
{
m.Unlock();
goto restart;
}

Ho guardato meglio come funzionano...e la Unlock non è mai boccante quindi per evitare attese attive bisogna bloccarsi sulla mutex (oppure su un'altra mutex, questo meccanismo lo devo ancora studiare) mentre il server preleva i dati...
Unlock(1) decrementa di uno la variaible all'interno del semaforo...

Per il server :

while(1)
{

m.Lock();
if(s.Lock(100))
{
prelevo i dati ed incremento i contatori
m.Unlock();
Sleep(rand()%5000); //serve per simulare l'elaborazione
}
else
m.Unlock();
}

Alla lock gli si può passare il timeout dopo il quale la lock esce e torna falso... Ripeto, anche qui bisognerebbe bloccarsi su un'altra mutex...

Fabrizio73
02-05-2002, 15:06
Allego a qs post il piccolo esperimento che ho fatto con le sezioni critiche ed i semafori. Un thread (classe Client) inserisce dei messaggi (classe Request) in una coda (classe Queue), un altro thread (classe Server) preleva i messaggi dalla coda e ne visualizza il contenuto.

PS: Sebbene abbia verificato che i due thread terminino, il programma termina molto tempo dopo :confused:!!! Perché :mad:???

Fabrizio73
02-05-2002, 15:10
Ooops... l'allegato...

cionci
02-05-2002, 18:52
Nel caso di + di un client il tuo esempio non funziona... Ti spiego il perchè...

Parto dalla struttare della mutex e del semaforo che hai usato :

// Thread A
semaphore.lock ();
mutex.lock ();
// ...
mutex.unlock ();


// Thread B
mutex.lock ();
// ...
mutex.unlock ();
semaphore.unlock ();

Allora....si parte con il semaforo a 0 (mettiamo di avere 4 elementi nella coda)...
Il thread A entra si blocca sulla lock...
Tutti i thread di tipo B vengono schedulati prima di A (metti che siano 5)...a turno si prendono la mutex e fanno tutti la unlock sul semaforo...
Il 5o fa un buffer overflow...perchè tenta di mettere un elemento nel 5o posto non presente nella coda...

Riguardo al fatto per cui ti si blocca....non ne ho idea...

cionci
02-05-2002, 19:03
Ho modificato la classe coda in questo modo :

class Queue : public CList <Request*, Request*>
{
public:
int count;
void put (Request* request) { cout << "aggiungo" << ++count << endl; AddTail (request);}
Request* get () { cout << "tolgo" << count << endl; return RemoveHead ();}
};

Ovviamente ho inizializzato a 0 il contatore...

Come potrai vedere si verifica la situazione che ti ho detto prima e count va oltre i 1000 che volevi...

Fabrizio73
02-05-2002, 21:58
Si... hai ragione ma credo che cmq la tua soluzione potrebbe portare ad un deadlock.

Non è così?

E se no, perché?

Fabrizio73
02-05-2002, 22:05
Per la cronaca... in Java per poter accedere all'analogo del semaforo (metodo wait () posseduto da tutti gli oggetti) è necessario possedere prima il lock sull'oggetto sul quale si vuole operare (in realtà è un monitor, implementato dalla primitiva synchronized). Per cui in Java si fa come dici tu. Tuttavia non si hanno deadlock, in quanto nel momento in cui si invoca la wait (), il lock sull'oggetto viene automaticamente rilasciato, e poi ridato nel momento in cui il thread riottiene l'utilizzo della CPU.

Per queste ragioni (l'estrema semplicità) io preferisco di gran lunga Java al C++ (VC++/MFC). Ammetto che Java sia molto carente nelle prestazioni velocistiche.

Infine vorrei far notare come Microsoft con la .NET Framework stia "copiando" le classi messe a disposizione da Java.

cionci
03-05-2002, 01:10
Originariamente inviato da Fabrizio73
[B]Si... hai ragione ma credo che cmq la tua soluzione potrebbe portare ad un deadlock.

Non è così?

E se no, perché?
Non c'è deadlock perchè la Unlock sul semaforo non è mai bloccante... Inoltre se non ottengo il lock dopo 100 ms (valore che può essere sicuramente abbassato) sul semaforo sblocco anche la mutex e quindi la mia regione critica torna ad essere accessibile...

Riguardo a Java...finchè si tratta di fare programmi di simulazione produttore/consumatore non cìè problemi, ma se uno deve realizzare un server internet multithreaded...realizzarlo in Java è secondo me un'utopia, soprattutto se si vuole sfruttare al massimo l'hardware a disposizione...

A .Net sinceramente non ho dato ancora un'occhiata...sembra che anche lui si appoggi ad un VM...boh vedremo...

Anche MFC non mi piace molto...preferisco non appoggiarmi a nessun framework anche se programmare applicazioni a finestre diventa praticamente impossibile...quindi se proprio dovessi fare un'applicazione seria penso che attualmente mi appoggerei a Borland C++ Builder o a Visual Basic...

Inoltre di questi tempi su windows ci programmo ben poco...

Fabrizio73
03-05-2002, 09:35
Originariamente inviato da cionci
[B]Nel caso di + di un client il tuo esempio non funziona... Ti spiego il perchè...

[...]

Allora....si parte con il semaforo a 0 (mettiamo di avere 4 elementi nella coda)...
Il thread A entra si blocca sulla lock...
Tutti i thread di tipo B vengono schedulati prima di A (metti che siano 5)...a turno si prendono la mutex e fanno tutti la unlock sul semaforo...
Il 5o fa un buffer overflow...perchè tenta di mettere un elemento nel 5o posto non presente nella coda...

Riguardo al fatto per cui ti si blocca....non ne ho idea...

Credo che in un programma fatto bene, le strutture dati siano dimensionate correttamente, in modo da evitare "overflow".

Originariamente inviato da cionci
[B]Non c'è deadlock perchè la Unlock sul semaforo non è mai bloccante... Inoltre se non ottengo il lock dopo 100 ms (valore che può essere sicuramente abbassato) sul semaforo sblocco anche la mutex e quindi la mia regione critica torna ad essere accessibile...

[...]

Ti riporto la descrizione del metodo Lock della classe CSyncObject (dalla quale discende CSemaphore), riportata nella MSDN Library:

Call this function to gain access to the resource controlled by the synchronization object. If the synchronization object is signaled, Lock will return successfully and the thread now owns the object. If the synchronization object is nonsignaled (unavailable), Lock will wait for the synchronization object to become signaled up to the number of milliseconds specified in the dwTimeOut parameter. If the synchronization object did not become signaled in the specified amount of time, Lock returns failure.

Se adoperassi il time-out praticamente effettuerei un'attesa (quasi) attiva.

cionci
03-05-2002, 18:58
EUREKA !!!

Ho trovato la soluzione infallibile, almeno sulla carta...

Queste sono le strutture per entrambi...

CMutex m(FALSE, "bufferMutex");
CSemaphore s(100, 100, "spazioDisponibile");
CSemaphore s2(0, 100, "messaggioDisponibile");


Produttore :

while(1)
{
s.Lock();
m.Lock();
//qui accedo al buffer e ai contatori
m.Unlock();
s2.Unlock();
}


Consumatore :

while(1)
{
s2.Lock();
m.Lock();
//qui accedo al buffer e ai contatori
m.Unlock();
s.Unlock();
}

Ecco qua... Avevo pensato all'introduzione di due semafori...ma devo dire la verità...sono andato a vedere sul mio libro di sistemi operativi (che prima o poi dovrò studiare) ;)

Per la coda puoi usare due mutex diverse fra produttore e consumatore...per permettere ad entrambi di accedere insieme alla coda...

Una mutex sul consumatore serve solo se sono + di uno...
Stessa cosa per il produttore...

Fabrizio73
03-05-2002, 19:49
1. Potresti spiegarmi la tua soluzione?

2. Che libro adoperi di Sistemi Operativi? "I Moderni Sistemi Operativi"? Non credo, è quello che ho adoperato io e la soluzione a due semafori non la ricordo!

Per la coda puoi usare due mutex diverse fra produttore e consumatore...per permettere ad entrambi di accedere insieme alla coda...

3. "Insieme" vuol dire contemporaneamente?

Io ho adoperato la soluzione che ho sempre sostenuto. Con essa ho realizzato un simulatore multi-thread di un sistema a coda M/M/1. Funziona (i risultati teorici sono approssimati da quelli ottenuta dalla simulazione) e non si blocca mai.

Allego il codice... dimmi che ne pensi!

cionci
03-05-2002, 20:28
1. In pratica funziona in questo modo...senza considerare la mutex...il produttore si può bloccare sul semaforo solo quando lo spazio disponibile sulla coda è esaurito...mentre il consumatore si blocca sul semaforo solo quando non ci sono messaggi dai produttori...

Inizialmente la coda è vuota, quindi il semaforo dello spazio disponibile è a 100, mentre quello del messaggio disponibile è a 0...

Quindi i consumatori si bloccano subito su "messaggioDisponibile"...
Un produttore decrementa (Lock) "spazioDisponibile" inserisce nel buffer un dato e sblocca i consumatori...
Gli eventuali altri produttori decrementano "spazioDisponible"...

Appena un produttore esegue la Unlock su "messaggioDisponibile" si sveglia un consumatore che prende il dato ed esegue la Unlock su "messaggioDisponibile"...questa Unlock sveglia gli eventuali consumatori bloccati perchè il buffer è pieno...

Ah...le Unlock non sono bloccanti...anzi non si fanno mai bloccanti...

Con questa tecnica non devi implementare alcun controllo di overflow sulla struttura dati....

2. Uso il libro del mio professore. "Principi e Tecniche di Programmazione Concorrente" P. Ancilotti (il mio prof), M. Boari

3. Ti parlo di coda circolare e non di lista...con la lista ci dovrei pensare...
Funziona perchè il consumatore accede solo all'elemento in cima alla coda, mentre il produttore solo a quello in fondo...
In caso di coda vuota il consumatore si blocca sul semaforo e non entrano in collisione sullo stesso elemento... Anche per il caso di coda piena il produttore si blocca sul semaforo...
Inoltre incremento variabili diverse...il consumatore incrementa la variaible che indica l'indice della testa della coda (il prossimo elemento da leggere dalla coda), mentre il produttore incrementa l'indice che ci dice il prossimo elemento da inserire in coda...

Per il tuo programma lo prendo e ci do un'occhiata...

cionci
04-05-2002, 19:20
Scusa, ma il semaforo che usi non limita gli ingressi sulla coda...
Hai messo INT_MAX...quindi la tua coda in teoria non ha limite... Chiaro che così funziona, ma non limiti superiormente l'uso di memoria da parte del tuo programma...
Che produttore/consumatore è se non c'è un limite sulla coda ?

Fabrizio73
05-05-2002, 10:48
Ma infatti io cercavo solo un metodo per notificare al server la presenza di messaggi in coda, evitando che il server effettuasse un'attesa attiva...

cionci
05-05-2002, 11:26
Comunque il metodo dei due semafori è spettacolare...non ci sarei mai arrivato da solo :)

Fabrizio73
05-05-2002, 11:42
Si... è molto singolare, infatti ho copiato il codice che mi hai fornito. Voglio studiarmelo con calma.

Ah... dimenticavo... grazie per l'aiuto ed i suggerimenti ;)

cionci
05-05-2002, 11:50
Figurati...mi è servito...diciamo che queste cose le avevo studiate, ma mai messe in pratica...

Fabrizio73
05-05-2002, 14:45
Hai mai sentito parlare di METERED SECTIONS?

Io mai!

Sono un curioso (ed utilissimo) incrocio fra semafori e sezioni critiche (o mutex, nel senso inteso da Microsoft).

Se sei interessato, qui (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndllpro/html/msdn_metrsect.asp) ne puoi trovare la descrizione.

cionci
05-05-2002, 19:54
No...è la prima volta che ne sento parlare...
Interessante...