Win32 multifile et synchronisation

Win32 multifile et synchronisation


Table des matières :

Introduction :

Ce cours d'instruction expliquera comment créer et contrôler les fils multiples dans Windows. C'est un cours d'instruction avancé ; le lecteur devrait être au courant de la programmation Win32.

Le besoin de programmes utilisateur-centraux sensibles est une bonne raison de créer les programmes qui emploient les fils multiples. En créant les fils multiples, un programme peut sembler faire beaucoup de choses immédiatement (sur un ordinateur avec plus d'une unité centrale de traitement, le programme réellement fera beaucoup de choses immédiatement). Par exemple, une unité de traitement de texte pourrait avoir un fil qui manipule la peinture de la fenêtre du processeur tandis qu'un autre fil sauve le document étant dactylographié toutes les quelques minutes tandis qu'encore un autre fil surveille activement tout le document pour des erreurs d'orthographe sans forcer l'utilisateur à attendre tandis que chaque tâche est accomplie.

Fond:

Sur une machine simple d'unité centrale de traitement Windows, tous les fils sont maintenus et donnés une tranche de temps de processeur (habituellement quelques millisecondes). Windows fera un cycle par la liste qu'il garde, faisant une pause et reprenant chaque fil. Une fois un fil a épuisé son temps sur le processeur, Windows fera une pause le fil, enregistrent ses registres d'unité centrale de traitement (avec quelques autres données), et, après reconstitution de toutes ses données, activent le prochain fil. Chaque fil a deux priorités : base et dynamique. La priorité basse peut être changée, cependant, elle ne peut pas être changée pour avoir une priorité plus haut que cela du fil de parent. La priorité dynamique est identique que la priorité basse, cependant, il peut être augmentée ou être abaissé par Windows. Windows choisit qui filètent pour fonctionner après basé sur la priorité dynamique du fil. Naturellement, les fils avec une priorité dynamique plus élevée fonctionnent d'abord.

Multifile:

Au commencement, chaque programme obtient un fil, connu sous le nom de fil primaire, qui est créé par le directeur d'objet de Windows. Le fil primaire peut être employé pour créer des fils d'enfant. Pour créer un autre appel CreateThread de fil. CreateThread prend les paramètres suivants

HANDLE CreateThread(

   LPSECURITY_ATTRIBUTES lpThreadAttributes, // access privileges

   DWORD dwStackSize,                        // initial stack size

   LPTHREAD_START_ROUTINE lpStartAddress,    // thread function

   LPVOID lpParameter,                       // thread argument

   DWORD dwCreationFlags,                    // creation option

   LPDWORD lpThreadId                        // thread identifier

);

      

Si le premier paramètre est passé en tant que NULLE, le fil obtiendra le descripteur de sécurité de défaut. Le deuxième paramètre te permet de placer la taille initiale de pile, vous peut passer 0 pour donner au fil la taille de pile de défaut. Le troisième paramètre est un indicateur à la fonction que le fil devrait commencer s'exécuter. Le quatrième paramètre tient tous les arguments que vous voulez passer à la fonction. Vous pouvez passer un 0 ou CREATE_SUSPENDED au cinquième paramètre. S'il est 0, le fil commence à fonctionner dès qu'il sera créé. Si la valeur est CREATE_SUSPENDED, le fil sera créé a suspendu et ne commencera pas à courir jusqu'à ce que vous appeliez ResumeThread. Le dernier paramètre est un indicateur à un DWORD qui tiendra l'identification unique du fil, après qu'il soit créé.

Si un fil était créé avec le drapeau de CREATE_SUSPENDED, vous voudrez appeler ResumeThread pour commencer à l'employer. ResumeThread prend le paramètre suivant :

DWORD ResumeThread(HANDLE hThread);

le hThread est la poignée reçue par CreateThread. À suspendez le fil encore, appel SuspendThread:

DWORD SuspendThread(HANDLE hThread);

De nouveau, le hThread est la poignée reçue près CreateThread.

Tandis qu'un fil est suspendu, vous pouvez vouloir soulever sa priorité basse et puis la réveiller encore. Un fil avec une priorité plus élevée obtiendra plus de temps de processeur. Pour changer la priorité basse d'un fil, appelez SetThreadPriority:

BOOL SetThreadPriority(

HANDLE hThread, // handle to the thread

int nPriority // thread priority level

);

le nPriority peut être l'une de sept valeurs :

THREAD_PRIORITY_LOWEST Deux niveaux au-dessous du processus
THREAD_PRIORITY_BELOW_NORMAL Un niveau au-dessous du processus
THREAD_PRIORITY_NORMAL Priorité normale
THREAD_PRIORITY_ABOVE_NORMAL Un niveau au-dessus du processus
THREAD_PRIORITY_HIGHEST Un niveau au-dessus du processus
THREAD_PRIORITY_TIME_CRITICAL Priorité de 15
THREAD_PRIORITY_IDLE Priorité de 1

 

Pour rechercher le niveau de priorité de base d'un fil, appelez GetThreadPriority:

int GetThreadPriority(HANDLE hThread);

Après que vous soyez fait utilisant un fil, vous pouvez vouloir le terminer. Pour faire ceci, appel ExitThread. ExitThread a suivi d'a CloseHandle est la manière gracieuse d'arrêter un fil. À arrêtez immédiatement un fil, appel TerminateThread. Spéc. de chaque fonction sont :

VOID ExitThread( DWORD dwExitCode);

 

BOOL CloseHandle(HANDLE hThread);

 

BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);


Windows appelle automatiquement ExitThread quand extrémités de fil sa fonction. le dwExitCode est le code de sortie que vous voulez passer. Le code de sortie peut plus tard être recherché utilisant GetExitCodeThread:

BOOL GetExitCodeThread(

HANDLE hThread,

LPDWORD lpExitCode // pointer to a DWORD to hold the exit code

);

Ce qui suit est un exemple de la façon employer des fils.

#define WIN32_LEAN_AND_MEAN

#include <windows.h>

#include <iostream>

DWORD ThreadID;

HANDLE TName;

void ExampleFunction()

{

for(int x=0; x<10; x++)

{

std:cout<<"Doing important stuff!\n";

}

}

void main()

{

TName= CreateThread(NULL, 0,

(LPTHREAD_START_ROUTINE)ExampleFunction,

NULL, CREATE_SUSPENDED, &ThreadID);

if (TName == NULL)

{

std::cout<<"Could not create thread!\n";

return false;

}

if(!SetThreadPriority(TName, THREAD_PRIORITY_BELOW_NORMAL))

{

std::cout<<"SetThreadPriority failed!\n";

return false;

}

if ((ResumeThread(TName)) == -1)

{

std::cout<<"ResumeThread failed!\n";

return false;

}

WaitForSingleObject(TName, INFINITE); // discussed later

CloseHandle(TName);

}

Synchronisation:

Décrivez ceci : un programme d'unité de traitement de texte crée deux fils, un à lisez un dossier et des autres pour écrire à un dossier. Tout est très bien au début, le premier fil attend le deuxième pour finir écrire au dossier avant la lecture il. Les deux fils fonctionnent heureusement, tout est très bien tant que l'écriture le fil écrit toujours d'abord. Mais pendant un jour sombre, le fil de lecture lit le dossier avant l'écriture le fil écrit au dossier et le programme échoue. C'est connu comme a condition de compétitivité parce que les deux fils emballent à finissez leur opération de dossier. Le programme échouera toujours si le fil de lecture gagne la course. Une condition de compétitivité est le résultat du mauvais synchronisation. Un autre problème est redouté impasse. Dans a impasse, les deux fils attendra l'un l'autre pour finir mais ni l'un ni l'autre fil ne finira jusqu'au autre finit d'abord, les entraînant devenir verrouillés. La solution à ceci le problème est d'employer un, ou plus, des objets de synchronisation fournis par Windows.

Pour gagner la propriété des objets l'uns des de synchronisation, un fil peut employer WaitForSingleObject, WaitForSingleObjectEx, WaitForMultipleObjects, ou WaitForMultipleObjectsEx.

WaitForSingleObject permet un fil à suspendez-vous tandis qu'il attend la propriété d'une de la synchronisation objets.

DWORD WaitForSingleObject(

HANDLE hHandle, // handle to object

DWORD dwMilliseconds // time-out interval

);

Le deuxième paramètre est le nombre de heures, en millisecondes, le fil est disposé à attendre l'objet avant qu'il retourne. Si le deuxième paramètre est placé à INFINI, la fonction pas temporisation. Si le deuxième paramètre est placé à 0, la fonction essayera de gagner la propriété de l'objet et de retourner immédiatement, même si elle n'a pas gagné la propriété.

WaitForSingleObjectEx est le même que WaitForSingleObject à moins que, il ajoute une plus d'option : alerte si l'opération d'entrée-sortie accomplit.

DWORD WaitForSingleObjectEx(

HANDLE hHandle, // handle to object

DWORD dwMilliseconds, // time-out interval

BOOL bAlertable // alertable option

);

Si le dernier paramètre est placé pour rectifier, la fonction retournera quand l'opération asynchrone d'entrée-sortie accomplit.

WaitForMultipleObjects permet un fil pour attendre les objets multiples de synchronisation immédiatement. Il peut être placé pour retourner quand quelques un ou tout des objets devenez disponible.

DWORD WaitForMultipleObjects(

DWORD nCount, // number of handles in array

CONST HANDLE *lpHandles, // object-handle array

BOOL bWaitAll, // wait option

DWORD dwMilliseconds // time-out interval

);

Si le troisième paramètre est placé POUR RECTIFIER, la fonction attendra tous les objets pour devenir disponible. Si le troisième paramètre est placé à FAUX, la fonction attendra les objets l'uns des pour devenir disponible, la valeur de retour de la fonction sera l'index à la rangée de poignées pour vous faire connaître quel objet qui a été obtenu.

WaitForMultipleObjectsEx est le même As WaitForSingleObjectEx, à moins qu'il vous permette à attente les objets multiples.

DWORD WaitForMultipleObjectsEx(

DWORD nCount, // number of handles in array

CONST HANDLE *lpHandles, // object-handle array

BOOL bWaitAll, // wait option

DWORD dwMilliseconds, // time-out interval

BOOL bAlertable // alertable option

);

Mutexes:

Comme tous autres objets de synchronisation, un mutex est créé par l'objet de Windows Directeur. Mutex est abréviation l'exclusion mutuelle, signifiant que seulement un bidon de fil le posséder à la fois. Pensez à lui comme une sorte de billet, en filètent tenir le billet obtient d'accéder à celui qui soit protégé par le mutex. Tandis que le fil est réalisant son travail, tous les autres fils attendent. Une fois le fil est fait, il donne le billet loin et le prochain fil peuvent faire ce qu'il doit faire tandis que l'autre attente de fils.

Pour créer un appel de mutex CreateMutex:

HANDLE CreateMutex(

LPSECURITY_ATTRIBUTES lpMutexAttributes,

BOOL bInitialOwner, // initial owner

LPCTSTR lpName // object's name

);

Le premier paramètre peut être passé comme NULLE pour obtenir les droits d'accès de défaut. Si le deuxième paramètre est placé POUR RECTIFIER, le créateur du mutex aura propriété d'abord. Le troisième paramètre est facultatif, il peut être employé à facilement identifiez le mutex.

Pour libérer le mutex après avoir fait quelque besoins d'être fait, appeliez ReleaseMutex.

BOOL ReleaseMutex(

HANDLE hMutex // handle to mutex

);

Seulement le fil qui possède le mutex peut le libérer. Pour détruire le mutex, appelez CloseHandle avec la poignée au mutex.

Sémaphores:

Une sémaphore est utile quand vous voulez permettre seulement une quantité limitée de fils à accédez à une ressource protégée à la fois. Avec un mutex, seulement un fil peut le posséder à tout moment donné. Avec une sémaphore, les fils multiples peuvent la posséder à la fois. En conséquence, n'importe quel fil peut également détruire la sémaphore. Pour créer une sémaphore appel CreateSemaphore.

HANDLE CreateSemaphore(

LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,

LONG lInitialCount, // initial count

LONG lMaximumCount, // maximum count

LPCTSTR lpName // object's name

);

Le premier paramètre est manipulé l'exact mêmes que le premier paramètre de CreateMutex. Le deuxième paramètre place le compte initial de la sémaphore, habituellement il est placé à la même valeur que celui du maximum compte. Le compte initial ne peut pas être moins que zéro ni plus grand que le maximum compte. Le troisième paramètre place le compte maximum limite dont le nombre filète qui peut posséder la sémaphore. Le dernier paramètre est manipulé la même manière en tant que cela de CreateMutex. Après des gains d'un fil la possession de la sémaphore le compte initial est décrémentée un. Si l'initiale le compte n'atteint 0, plus de fils peut gagner la possession de la sémaphore. le compte initial est incrémenté après qu'un fil libère la sémaphore.

Pour libérer une sémaphore, appelez ReleaseSemaphore.

BOOL ReleaseSemaphore(

HANDLE hSemaphore, // handle to semaphore

LONG lReleaseCount, // count increment amount

LPLONG lpPreviousCount // previous count

);

Le deuxième paramètre place par combien le compte devrait être incrémenté, habituellement c'est 1. Le troisième paramètre tient un indicateur sur une variable qui sera remplie avec le compte précédent, ensuite ReleaseSemaphore accomplit.

Sections critiques:

Une section critique est très semblable à un mutex. Une section critique peut seulement être possédé par un fil à la fois, cependant, une section critique ne peut pas être partagé entre les processus, un mutex peut. Pour cette raison, travaux d'une section critique plus rapidement. Pour créer une section critique, appelez InitializeCriticalSection; au propre une section critique, appel EnterCriticalSection; pour abandonner la propriété d'a section critique, appel LeaveCriticalSection; à détruisez une section critique, appel DeleteCriticalSection.

VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

 

VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);


VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);


VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

 

Toutes les fonctions exigent un indicateur à l'objet de section critique. Vous déclarez un comme ceci :

CRITICAL_SECTION cs;


Toutes les fonctions exigent un indicateur à l'objet de section critique. Vous déclarez un comme ceci :

HANDLE CreateEvent(

LPSECURITY_ATTRIBUTES lpEventAttributes,

BOOL bManualReset, // reset type

BOOL bInitialState, // initial state

LPCTSTR lpName // object name

);


Si le deuxième paramètre est placé POUR RECTIFIER, l'événement doit être remis à zéro manuellement. Si le deuxième paramètre est placé à FAUX, l'événement reviendra à son état unsignaled juste après un appel à SetEvent. Si le tiers le paramètre est placé POUR RECTIFIER, l'événement sera créé et placé au signalé état. Le dernier paramètre est un nom facultatif pour l'objet.

SetEvent, ResetEvent, et PulseEvent sont tous appelés de la même manière :

BOOL SetEvent(HANDLE hEvent);

 

BOOL ResetEvent(HANDLE hEvent);

 

BOOL PulseEvent(HANDLE hEvent);

 

être hEvent la poignée retournée de CreateEvent.

La combinaison d'un mutex et d'un événement est une bonne manière d'éviter impasses. Ce qui suit est un exemple d'employer le mutex et l'événement objets de synchronisation.

#define WIN32_LEAN_AND_MEAN

#include <windows.h>

#include <iostream>

HANDLE hMutex, hWriteDone, hReadDone;

int num, state;

void Writer()
{
for(int x=10; x>=0; x--)
{

while (true)
{

if (WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED)
{

std::cout<<"In writing loop, no mutex!\n";

ExitThread(0);
}

if (state == 0)
{

ReleaseMutex(hMutex);

WaitForSingleObject(hReadDone, INFINITE);

continue;
}
break;
}
std::cout<<"Write done\n";

num= x;

state= 0;

ReleaseMutex(hMutex);

PulseEvent(hWriteDone);
}
}

void Reader()
{
while(true)
{

if (WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED)
{
std::cout<<"In reader, no mutex!\n";

ExitThread(0);

}
if (state == 1)
{
ReleaseMutex(hMutex);

WaitForSingleObject(hWriteDone, INFINITE);

continue;
}

if (num == 0)
{

std::cout<<"End of data\n";

ReleaseMutex(hMutex);

ExitThread(0);
}
else {

std::cout<<"Read done\n";

state=1;

ReleaseMutex(hMutex);

PulseEvent(hReadDone);

}
}
}

void main()
{

HANDLE TName[2];
DWORD ThreadID;

state= 1;

hMutex= CreateMutex(NULL, FALSE, NULL);
hWriteDone= CreateEvent(NULL, TRUE, FALSE, NULL);
hReadDone= CreateEvent(NULL, TRUE, FALSE, NULL);

TName[0]= CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)Writer,
NULL, 0, &ThreadID);

TName[1]= CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)Reader,
NULL, 0, &ThreadID);

WaitForMultipleObjects(2, TName, TRUE, INFINITE);
CloseHandle(TName);
}

Références:


Pick a language
English  French