• Nem Talált Eredményt

Egy rosszul felépített rendszerben előfordulhat az a helyzet, hogy minden filozófus egyszerre éhezik meg.

Tegyük fel, hogy a filozófusok a 4.2. ábrán leírt módon kezdenek étkezni!

4.2. ábra. Evés algoritmusa

A gond a következő: előfordulhat olyan eset, hogy minden filozófus egyszerre kezd el étkezni. Mi történik ekkor? Ha a fenti algoritmus közel egy időben kezd el végrehajtódni minden filozófus esetén, akkor első lépésben mindegyik filozófus egy időben néz le az asztalára, és maga előtt látja a saját pálcikáját. Felveszi, magasba emeli. Majd átnéz a szomszédjához, de annak már nincs lenn a pálcika. Várakozni kezd hát. De meddig?

A várakozás addig tart, amíg a szomszéd a pálcikát le nem rakja. Ez általában hamar bekövetkezik, hiszen a szomszéd általában eszik. Amikor befejezi, lerakja a pálcikát. Csakhogy most a szomszéd nem eszik, ő is várakozik.

Képzeljük el a fenti szituációt két filozófus esetén. Mindkettő felemelte a saját pálcáját, és mindkettő várja, hogy a másik lerakja végre a második pálcikát. Az algoritmus szerint mindkét filozófus ekkor a várakozó ciklusba fog beragadni az idők végtelenségéig.

Ezt a helyzetet nevezi az informatika deadlock-nak, amit halálos szorításnak fordíthatnánk, de a hivatalos terminológia szerint ezt holtpontra magyarosítottuk.

A holtpont fellépéséhez legalább két folyamatra van szükség: és . Holtpont akkor lép fel, amikor folyamat várakozik a folyamat valamely állapotváltozására, miközben a a állapotváltozására vár. Ha mindkét folyamat hajlamos a végtelen várakozásra, akkor ez mindkét folyamat végtelen ideig történő várakozásához vezet.

A filozófusok esetén (hétköznapi esetben) a megoldás egyszerű: valamelyik filozófusnak majd csak eszébe jut, hogy megnézze, mit csinálnak a többiek. Amennyiben észreveszi a problémát, és belátja, hogy valakinek meg kell szakítani a várakozást, és hajlandó is feláldozni magát, úgy lerakja a saját pálcikáját egy pillanatra. Ezzel megtöri a várakozást, és láncreakciót indít el, hiszen ekkor az ő szomszédja felveszi, étkezik, és lerakja mindkét pálcikát. Ekkor a következő filozófus tud majd enni, és ha már mindenki evett (körbe ért), akkor az áldozatot vállaló filozófus is étkezhet végre.

Az informatikában hasonló helyzet áll elő az Ethernet hálózatok esetén. Ott egy HUB-ra kapcsolva több hálózati kártya is fellelhető, melyek a HUB-on keresztüli összekapcsolás révén úgy is elképzelhetőek, mintha egyetlen kábelre lenne felfűzve mindegyik hálózati eszköz. A probléma akkor lép fel, amikor egy időben több kártya is kezdeményezni szeretne hálózati forgalmat, adatcsomagot kívánna küldeni. Mivel egy kábelen egy időben zavarásmentesen csak egy csomag közlekedhet, egyik kártyának sem sikerülne az üzenetküldés.

Nem lenne megoldás, ha ekkor a kártyák mindegyike ugyanannyi ideig kezdene várakozni, majd újra próbálkozna az üzenetküldéssel – hiszen akkor az újra meghiúsulna. Ehelyett a kártyák sorsot húznak, véletlen (random) ideig várakozni kezdenek. Az egyik kártya valószínűleg rövidebb időt kap a véletlen értékek közül, így a rövid várakozás után neki már sikerülni fog az üzenetküldés, amíg a másik kártya még csendben

várakozik. Ha mindkét kártya ugyanannyi ideig várakozik random módon, akkor persze a második próbálkozás is kudarc lesz. Ekkor újra választanak maguknak várakozási időt, most már egy nagyobb intervallumból (kisebb az esély az egyforma értékekre). Ezt csak néhányszor vállalják fel, ha annyi idő alatt nem sikerül az üzenetküldés, akkor feladják.

Ezen megoldás érdekessége, hogy a két részt vevő kártya miután detektálja a problémát, nem cserélnek egymással információt, mégis megpróbálják megoldani a deadlockot. Egyszerűbb ötletnek tűnne, hogy a két kártya beszélje meg, ki mennyi ideig várakozzon, és állapodjanak meg egy eltérő időben. Csakhogy nyilván ez nem tud működni ebben az esetben, hiszen a két kártya pont azért került összeütközésbe, mert egyszerre kívántak adatforgalmazni ugyanazon az átvivő közegen, így egymással sem tudnak adatcsomagot cserélni.

Másrészt minden ilyen megbeszélés során valamelyiknek engednie kell a másik javára. Egyik kártyának vezető beosztásba kell kerülnie, hogy a döntését és akaratát a másik kártyára rákényszeríthesse. A vezetőválasztás újabb üzenetváltásokat jelentene, így összességében nem lennénk korábban készen.

A deadlock szituáció könnyen fellép többszálú programok esetén akkor, ha mindkét szál két zárat is próbál szerezni – de eltérő sorrendben (lásd a 4.3. ábra). Amennyiben a külső lock-ot a két szál nagyjából egy időben éri el, mindkettő megszerzi a számára első lockot, akkor a második megszerzése már reménytelen.

4.3. ábra. Deadlock két szál között

4.1. videó. Deadlock teszt

A 4.1. videón látható, amint a két szál próbál különböző sorrendben zárolni a két erőforrást. A 11. tesztfutás során kialakul a holtpont.

Ezen futási eredmény egyébként ritka. A két szálnak egy időben kell elérni a külső lock utasítást. Ha az egyik kicsit gyorsabb, megszerzi mindkét lockot mielőtt a második belekezdene a saját külső lockjába, akkor máris rendben vagyunk, hiszen a külső lockját sem tudja megszerezni, és várakozni kezd. Amikor a gyorsabb elkészül, már mindkét zárat felszabadítja, így a lassúbb szál is meg tudja mindkettőt szerezni.

Ezt úgy kell elképzelni, hogy a programunk 50 indításból 50 esetben hibátlannak bizonyul. Az 51-edik alkalommal deadlock alakul ki, és lefagy. Az újabb tesztelések azonban megint csak képtelenek ezt az esetet reprodukálni. Ha a programot elkezdjük debugolni, lépésenként végrehajtani, szinte biztos, hogy nem lesz pontosan ez az eset, hiszen ekkor a szálváltások biztosan nem ugyanakkor következnek be, mint valós futási környezetben. A nehezen vagy egyáltalán nem reprodukálható hibák a tesztelők és a fejlesztők rémálma. Persze léteznek olyan eszközök, melyek nagy biztonsággal képesek a kód elemzésével megjósolni, hogy a program hordozza-e a deadlock kialakulásának esélyét. Amennyiben a fejlesztést formális specifikáció és bizonyított programtulajdonságok alapján végezzük, úgy a deadlockmentességet formális eszközökkel bizonyítani kell.