N∞kdy je pot°eba, aby se n∞jakß Φinnost provßd∞la na pozadφ nezßvisle na hlavnφm programu, nap°φklad n∞jak² slo₧it² v²poΦet. Prßv∞ k takovΘmu ·Φelu se hodφ pracovnφ vlßkna. Ka₧d² program mß nejmΘn∞ jedno vlßkno a to hlavnφ vlßkno programu. O to se mßlokdy starßme a v∞tÜina zaΦφnajφcφch programßtor∙ o tom ani nevφ, ale kdy₧ pracujeme s vlßkny tak na to musφme myslet, aby nedoÜlo ke kolizi. Proto₧e, kdy₧ spustφme pracovnφ vlßkno nemßme jedno vlßkno, ale dv∞ vlßkna.
Jak vlßkna vlastn∞ pracujφ? Vlßkna neb∞₧φ souΦasn∞ jak si myslφ n∞kte°φ lidΘ, ale jejich provßd∞nφ se st°φdß. Tj. Chvφli se provßdφ jedno vlßkno, potom se jeho provßd∞nφ pozastavφ a spustφ se provßd∞nφ vlßkna se stejnou nebo vyÜÜφ prioritou a tak dßle. Te∩ by jeÜt∞ cht∞lo vysv∞tlit co jsou to priority vlßken. Priorita urΦuje v jakΘm po°adφ se budou vlßkna provßd∞t. Pamatujte si, ₧e se nikdy nespustφ vlßkno s ni₧Üφ prioritou, kdy₧ Φekß na provßd∞nφ vlßkno s vyÜÜφ prioritou.
JeÜt∞ ne₧ ukß₧i jak se pracovnφ vlßkna vytvß°ejφ musφm vßm °φci
n∞co o synchronizaci. Velice Φasto se stßvß, ₧e ve vφce r∙zn²ch vlßknech
p°istupujeme ke stejn²m prom∞nn²m. Potom se nßm m∙₧e stßt, ₧e jedno vlßkno
zaΦne p°i°azovat hodnotu do jednΘ z t∞chto prom∞nn²ch a p°esn∞ v
polovin∞ tΘto operace (dejme tomu ₧e se nastavφ jeden ze dvou byt∙
dvoubytovΘ prom∞nnΘ) se toto vlßkno pozastavφ a spustφ se druhΘ vlßkno,
kterΘ Φte (nebo zapisuje) hodnotu tΘ samΘ prom∞nnΘ. Potom v tomto vlßkn∞
zφskßme nesmyslnou hodnotu. Aby se zabrßnilo t∞mto p°φhodßm musφme vlßkna
synchronizovat. Pro synchronizaci v rßmci jednΘ aplikace je nejjednoduÜÜφ
pou₧φt kritickΘ sekce. Ty d∞lajφ v podstat∞ to, ₧e nedovolφ provßd∞nφ
k≤du jinΘho vlßkna chrßn∞nΘho tou samou kritickou sekcφ, dokud se nedokonΦφ
provßd∞nφ k≤du v aktußlnφm vlßku chrßn∞nΘho kritickou sekcφ. (To vysv∞tlenφ
se mi moc nepovedlo, ale nebojte, vÜe bude jasnΘ z p°φkladu, kter² bude nßsledovat.)
JeÜt∞ jedna poznßmka. KritickΘ sekce by se m∞li definovat jako globßlnφ
prom∞nnΘ, aby k nim byl snadn² p°φstup odkudkoli.
KritickΘ sekce zapouzd°uje t°φda CCriticalSection. Ta mß dv∞ ΦlenskΘ
prom∞nnΘ Lock() - pro oznaΦenφ zaΦßtku chrßn∞nΘ Φßsti k≤du
(uzamΦenφ) a Unlock() - pro oznaΦenφ konce chrßn∞nΘ Φßsti k≤du
(odemΦenφ).
Existujφ i dalÜφ synchronizaΦnφ objekny jako mutexy, semafory a dalÜφ. Pokud se chcete dozv∞d∞t o synchronizaci vφce, doporuΦuji strßnky www.eternal.cz a to konkrΘtn∞ Φlßnky: Synchronizace pomocφ kritickΘ sekce, Synchronized jako v Java, Synchro objekt Semaphore, Synchro objekt Mutex, Objekt Event, ╚ekßnφ a synchronizace.
A koneΦn∞ k vlastnφm pracovnφm vlßkn∙m. Pracovnφ vlßkno je vlastn∞ reprezentovßno funkcφ, jejφ₧ provßd∞nφ ukonΦφ i provßd∞nφ vlßkna. Tato funkce mß pevn∞ dan² funkΦnφ prototyp a to:
UINT MojeFunkceVlakna( LPVOID pParam );
SpuÜt∞nφ vlßkna se provede pomocφ funkce AfxBeginThread, kterΘ se p°edß jako jeden z parametr∙ ukazatel na void (LPVOID), kter² je p°edßn naÜφ funkci vlßkna. Tato funkce vracφ ukazatel na t°φdu CWinThread, co₧ je t°φda zapouzd°ujφcφ vlßkna. V MFC se pro ka₧dΘ vlßkno vytvo°φ instance tΘto t°φdy, kterß je po skonΦenφ vlßkna automaticky smazßna. Jejφmu smazßnφ m∙₧ete zabrßnit tφm, ₧e nastavφte jejφ Φlenskou prom∞nnou m_bAutoDelete na FALSE. Pomocφ tΘto instance t°φdy m∙₧ete vlßkno ovlßdat z vn∞jÜku vlßkna. Uvnit° vlßkna m∙₧ete tento ukazatel zφskat pomocφ funkce AfxGetThread. Volßnφm ΦlenskΘ funkce m∙₧ete vlßkno pozastavit - SuspendThread, znovu spustit - ResumeThread, zm∞nit prioritu vlßkna - SetThreadPriority a tak dßle.
Nßsleduje okomentovan² p°φklad, ve kterΘm se v hlavnφm vlßkn∞ (vlastnφ aplikace - funkce main) vypisuje na obrazovku hodnota prom∞nnΘ dokud tato prom∞nnß nedosßhne urΦitΘ hodnoty. Hodnota tΘto prom∞nnΘ je zvyÜovßna uvnit° pracovnφho vlßkna. (P°φklad je sice klasickß konzolovß aplikace, ale abyste ji mohli p°elo₧it pot°ebujete mφt zapnutou podporu MFC. To znamenß pou₧φt Φtvrtou mo₧nost AppWizardu pro konzolovΘ aplikace - An aplication that support MFC. To je kv∙li pou₧it²m funkcφm. S vlßkny lze samoz°ejm∞ pracovat i v ΦistΘm C - Φku, ale to se musφ pou₧φt jinΘ funkce.)
// vlakna.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <afxmt.h>// hlaviΦkov² soubor pro synchronizaΦnφ t°φdy
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// The one and only application object
CCriticalSection cs;//deklarace globßlnφ prom∞nnΘ kritickΘ sekce
//Funkce vlßkna
UINT Vlakno(LPVOID pParam)
{
int* pProm = (int*)pParam; //Konverze parametru funkce na p∙vodnφ typ
int i; //Pomocnß prom∞nnß
for(i=0; i<=10; i++)
{
cs.Lock(); //Vstup (uzamΦenφ) do kritickΘ sekce
/*
Pokud by se v druhΘm vlßkn∞ v tomto okam₧iku zavolalo cs.Lock(), bude druhΘ vlßkno
pozastaveno dokud se v tomto vlßkn∞ nezavolß cs.Unlock()
*/
*pProm = i; //P°i°azenφ hodnoty do prom∞nnΘ p°edanΘ parametrem
cs.Unlock(); //Konec (odemΦenφ) kritickΘ sekce
Sleep(1); //Uspßnφ vlßkna na 1 milisekundu
}
return 0;
}
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
}
int pom,x;
x = 0;
// SpuÜt∞nφ vlßkna
CWinThread* pThread = AfxBeginThread(Vlakno, // ukazatel na funkci vlßkna
&x, // ukazatel na prom∞nnou x - parametr funkce vlßkna
THREAD_PRIORITY_NORMAL); // priorita vlßkna - normßlnφ, stejnß jako hlavnφho vlßkna
while(1)
{
cs.Lock(); // vstup do kritickΘ sekce
pom = x; // p°i°azenφ hodnoty v x do pomocnΘ prom∞nnΘ, aby byla
// uzamΦena co nejmenÜφ Φßst k≤du
cs.Unlock(); // konec kritickΘ sekce
printf("%d\n",pom); // v²pis hodnoty
if (pom==10) break; // pokud jsme dosßhli Φφsla 10 skonΦφme
}
return nRetCode;
}
Pokud chcete o pracovnφch vlßknech v∞d∞t vφc ne₧ jsou tyto minimßlnφ informace, podφvejte se na www.eternal.cz na Φlßnek Multithreading (1. a₧ 6. dφl).