Ինչ նպատակների համար են օգտագործվում բազմաթել համակարգերը: Ութ պարզ կանոն բազմաշերտ ծրագրերի մշակման համար

Ո՞ր թեման է ամենից շատ հարցեր և դժվարություններ առաջացնում սկսնակների համար: Երբ ես այս մասին հարցրեցի իմ ուսուցչին և Java ծրագրավորող Ալեքսանդր Պրյախինին, նա անմիջապես պատասխանեց. «Բազմալեզու»: Շնորհակալություն նրան այս հոդվածի պատրաստման գաղափարի և օգնության համար:

Մենք կդիտարկենք հավելվածի ներքին աշխարհը և դրա գործընթացները, կպարզենք, թե որն է բազմաթելերի էությունը, երբ է դա օգտակար և ինչպես իրականացնել այն `օրինակ օգտագործելով Java- ն: Եթե ​​սովորում եք OOP- ի այլ լեզու, մի անհանգստացեք. Հիմնական սկզբունքները նույնն են:

Հոսքերի և դրանց ծագման մասին

Բազմաթերթը հասկանալու համար նախ հասկանանք, թե ինչ է գործընթացը: Գործընթացը վիրտուալ հիշողության և ռեսուրսների մի մասն է, որը OS- ն հատկացնում է ծրագիր գործարկելու համար: Եթե ​​միևնույն հավելվածի մի քանի օրինակ բացեք, համակարգը յուրաքանչյուրի համար գործընթաց կհատկացնի: Modernամանակակից բրաուզերներում յուրաքանչյուր ներդիրի համար կարող է լինել առանձին գործընթաց:

Դուք հավանաբար հանդիպել եք Windows- ի «Task Manager» - ին (Linux- ում դա «System Monitor» է) և գիտեք, որ անհարկի ընթացող գործընթացները բեռնում են համակարգը, և դրանցից ամեն «ծանր» -ը հաճախ սառչում են, ուստի դրանք հարկադրաբար պետք է դադարեցվեն: .

Բայց օգտվողները սիրում են բազմակողմանի աշխատանքը. Մի կերակրիր նրանց հացով, թող բացեն տասնյակ պատուհաններ և ցատկեն այս ու այն կողմ: Կա երկընտրանք. Դուք պետք է ապահովեք ծրագրերի միաժամանակյա աշխատանքը և միևնույն ժամանակ նվազեցնեք համակարգի բեռը, որպեսզի այն չդանդաղի: Ենթադրենք, սարքավորումները չեն կարող հետևել սեփականատերերի կարիքներին. Դուք պետք է հարցը լուծեք ծրագրակազմի մակարդակով:

Մենք ցանկանում ենք, որ պրոցեսորը կատարի ավելի շատ հրահանգներ և ավելի շատ տվյալներ մշակի ժամանակի միավորի համար: Այսինքն, մենք պետք է ավելի շատ կատարված ծածկագիր տեղավորենք յուրաքանչյուր անգամ: Մտածեք կոդի կատարման միավորը որպես օբյեկտ. Դա թել է:

Բարդ գործին ավելի հեշտ է մոտենալ, եթե այն բաժանում ես մի քանի պարզի: Հիշողության հետ աշխատելիս այդպես է. «Ծանր» գործընթացը բաժանված է թելերի, որոնք ավելի քիչ ռեսուրսներ են խլում և ավելի հավանական է, որ ծածկագիրը փոխանցեն հաշվիչին (որքանով է ճշգրիտ - տե՛ս ստորև):

Յուրաքանչյուր ծրագիր ունի առնվազն մեկ գործընթաց, և յուրաքանչյուր գործընթաց ունի առնվազն մեկ թել, որը կոչվում է հիմնական թել, և որից անհրաժեշտության դեպքում գործարկվում են նորերը:

Թելերի և գործընթացների միջև տարբերությունը

    Թեմաներն օգտագործում են գործընթացի համար հատկացված հիշողությունը, և գործընթացները պահանջում են իրենց հիշողության տարածքը: Հետևաբար, թելերը ստեղծվում և ավարտվում են ավելի արագ. Համակարգը կարիք չունի ամեն անգամ նրանց հասցեների նոր տարածք հատկացնել, այնուհետև թողարկել այն:

    Յուրաքանչյուրն աշխատում է իր տվյալների հետ. Նրանք կարող են ինչ -որ բան փոխանակել միայն միջմշակութային հաղորդակցության մեխանիզմի միջոցով: Թեմաներն ուղղակիորեն մուտք են գործում միմյանց տվյալներ և ռեսուրսներ. Այն, ինչ փոխվել է, անմիջապես հասանելի է բոլորին: Թելը կարող է վերահսկել գործընթացում գտնվող «ընկերոջը», մինչդեռ գործընթացը վերահսկում է բացառապես իր «դուստրերին»: Հետևաբար, հոսքերի միջև անցումն ավելի արագ է, և նրանց միջև հաղորդակցությունն ավելի հեշտ է:

Ի՞նչ եզրակացություն է սա անում: Եթե ​​Ձեզ անհրաժեշտ է հնարավորինս արագ մշակել մեծ քանակությամբ տվյալներ, դրանք բաժանեք կտորների, որոնք կարող են մշակվել առանձին թելերով, այնուհետև արդյունքը համատեղեք: Դա ավելի լավ է, քան ռեսուրսների կարիք ունեցող գործընթացների ձվադրումը:

Բայց ինչո՞ւ է Firefox- ի նման հանրաճանաչ հավելվածը գնում բազմաթիվ գործընթացներ ստեղծելու ճանապարհով: Քանի որ դիտարկիչի համար մեկուսացված ներդիրների աշխատանքը հուսալի և ճկուն է: Եթե ​​ինչ -որ բան սխալ է մեկ գործընթացում, ապա անհրաժեշտ չէ ամբողջ ծրագիրը դադարեցնել. Հնարավոր է պահպանել տվյալների առնվազն մի մասը:

Ինչ է բազմալեզու

Այսպիսով, մենք գալիս ենք հիմնական կետին: Բազմաթղթավորումն այն է, երբ կիրառման գործընթացը բաժանվում է թելերի, որոնք մշակվում են զուգահեռաբար `ժամանակի մեկ միավորի վրա, պրոցեսորի կողմից:

Հաշվարկային բեռը բաշխվում է երկու կամ ավելի միջուկների միջև, այնպես որ ինտերֆեյսը և ծրագրի այլ բաղադրիչները չեն դանդաղեցնում միմյանց աշխատանքը:

Բազմաթելային ծրագրերը կարող են գործարկվել նաև մեկ միջուկային պրոցեսորների վրա, սակայն այնուհետև թելերը կատարվում են հերթով. Առաջինը աշխատել է, նրա վիճակը պահպանվել է. Երկրորդը թույլատրվել է աշխատել, պահպանվել. Վերադարձվել է առաջինին կամ գործարկվել երրորդը եւ այլն

Busբաղված մարդիկ դժգոհում են, որ իրենք ընդամենը երկու ձեռք ունեն: Գործընթացներն ու ծրագրերը կարող են ունենալ այնքան ձեռքեր, որքան անհրաժեշտ է առաջադրանքը հնարավորինս արագ կատարելու համար:

Սպասեք ազդանշանի. Համաժամեցում բազմաթելային ծրագրերում

Պատկերացրեք, որ մի քանի թել փորձում են միևնույն տվյալների տարածքը փոխել: Ո՞ւմ փոփոխություններն են ի վերջո ընդունվելու և ո՞ւմ փոփոխությունները են չեղարկվելու: Համատեղ ռեսուրսների հետ շփոթվելուց խուսափելու համար թեմաները պետք է համակարգեն իրենց գործողությունները: Դա անելու համար նրանք ազդակներ են փոխանակում ՝ օգտագործելով ազդանշաններ: Յուրաքանչյուր թեմա պատմում է մյուսներին, թե ինչ է անում և ինչ փոփոխություններ է սպասվում: Այսպիսով, ռեսուրսների ներկա վիճակի վերաբերյալ բոլոր թելերի տվյալները համաժամեցված են:

Հիմնական համաժամացման գործիքներ

Փոխադարձ բացառումը (փոխադարձ բացառում, կրճատ - mutex) - «դրոշ», որը գնում է այն շարանը, որն այժմ թույլատրված է աշխատել ընդհանուր ռեսուրսներով: Վերացնում է այլ թելերի մուտքը գրավված հիշողության տարածք: Applicationրագրում կարող են լինել մի քանի մուտեքս, և դրանք կարող են բաշխվել գործընթացների միջև: Մուտեքսը դիմումին ստիպում է ամեն անգամ մուտք գործել օպերացիոն համակարգի միջուկ, ինչը ծախսատար է:

Սեմալիստ - թույլ է տալիս սահմանափակել թելերի քանակը, որոնք տվյալ պահին կարող են մուտք գործել ռեսուրս: Սա կնվազեցնի պրոցեսորի բեռը, երբ ծածկագրեր կան, որտեղ կան նեղություններ: Խնդիրն այն է, որ թելերի օպտիմալ քանակը կախված է օգտագործողի մեքենայից:

Իրադարձություն - Դուք սահմանում եք մի պայման, որի առաջացման դեպքում վերահսկողությունը փոխանցվում է ցանկալի շարանին: Հոսքերը փոխանակում են իրադարձությունների տվյալները ՝ զարգացնելու և տրամաբանորեն շարունակելու միմյանց գործողությունները: Մեկը ստացավ տվյալները, մյուսը ստուգեց դրանց ճշգրտությունը, երրորդը պահեց այն կոշտ սկավառակի վրա: Միջոցառումները տարբերվում են չեղյալ հայտարարվելու եղանակով: Եթե ​​Ձեզ անհրաժեշտ է մի քանի թեմա տեղեկացնել իրադարձության մասին, ապա ստիպված կլինեք ձեռքով կարգավորել չեղարկման գործառույթը ՝ ազդանշանը դադարեցնելու համար: Եթե ​​կա միայն մեկ թիրախային թեմա, կարող եք ստեղծել ավտոմատ վերակայման իրադարձություն: Այն հոսանքին հասնելուց հետո ինքը կդադարեցնի ազդանշանը: Eventկուն հոսքի վերահսկման համար իրադարձությունները կարող են հերթագրվել:

Քննադատական ​​բաժին - ավելի բարդ մեխանիզմ, որը միավորում է հանգույցի հաշվիչը և սեմաֆորը: Հաշվիչը թույլ է տալիս հետաձգել սեմալիստի սկիզբը ցանկալի ժամանակով: Առավելությունն այն է, որ միջուկը ակտիվանում է միայն այն դեպքում, երբ հատվածը զբաղված է, և սեմալիստը պետք է միացնել: Մնացած ժամանակը, շարանը աշխատում է օգտագործողի ռեժիմում: Ավաղ, հատվածը կարող է օգտագործվել միայն մեկ գործընթացի շրջանակներում:

Ինչպես իրականացնել բազմալեզու ծրագրավորում Java- ում

Թեման դասը պատասխանատու է Java- ում թելերով աշխատելու համար: Առաջադրանք կատարելու համար նոր թեմա ստեղծել նշանակում է ստեղծել թեմա դասի օրինակ և այն կապել ձեր ուզած ծածկագրի հետ: Դա կարելի է անել երկու եղանակով.

    ենթադաս Թեմա;

    ներդնել Runnable ինտերֆեյսը ձեր դասարանում, այնուհետև դասի օրինակները փոխանցեք Thread կոնստրուկտորին:

Թեև մենք չենք անդրադառնա փակուղիների թեմային, երբ թելերը արգելափակում են միմյանց աշխատանքը և սառչում, մենք դա կթողնենք հաջորդ հոդվածի համար:

Java բազմալեզու օրինակ. Պինգ-պոնգ մութեքսներով

Եթե ​​կարծում եք, որ սարսափելի բան է սպասվում, շնչեք: Մենք կքննարկենք համաժամացման օբյեկտների հետ գրեթե խաղային եղանակով աշխատել. Երկու թել կթափվի մութեքսով: Բայց իրականում դուք կտեսնեք իրական ծրագիր, որտեղ միայն մեկ շարանը կարող է միաժամանակ մշակել հանրորեն հասանելի տվյալները:

Նախ, եկեք ստեղծենք դաս, որը ժառանգում է արդեն մեզ հայտնի Thread- ի հատկությունները և գրում kickBall մեթոդ.

PingPongThread- ի հանրային դասը տարածում է Thread (PingPongThread (String name) (this.setName (name); // override the thread name) @Override public void run () (Ball ball = Ball.getBall (); while (ball.isInGame () ) (kickBall (գնդակ);))) մասնավոր անվավեր kickBall (գնդակ) (եթե (! ball.getSide (). հավասար է (getName ())) (ball.kick (getName ());))))

Հիմա եկեք հոգանք գնդակի մասին: Նա մեզ հետ կլինի ոչ թե պարզ, այլ հիշարժան, որպեսզի կարողանա ասել, թե ով է հարվածել, որ կողմից և քանի անգամ: Դա անելու համար մենք օգտագործում ենք mutex. Այն տեղեկատվություն կհավաքի թելերից յուրաքանչյուրի աշխատանքի մասին. Դա թույլ կտա մեկուսացված թելերին հաղորդակցվել միմյանց հետ: 15 -րդ հարվածից հետո մենք գնդակը խաղից դուրս կբերենք, որպեսզի այն լուրջ վնասվածքներ չունենանք:

Հանրային դասի գնդակ (մասնավոր int մեկնարկում = 0; մասնավոր ստատիկ գնդակի օրինակ = նոր գնդակ (); մասնավոր լարային կողմ = ""; մասնավոր գնդակ () () ստատիկ գնդակ getBall () (վերադարձի օրինակ;) համաժամեցված դատարկ հարված (լարային խաղացանկ) (հարվածներ ++; կողմ = խաղացանկ; System.out.println (մեկնարկներ + "" + կողմ);) String getSide () (վերադարձի կողմ;) բուլյան isInGame () (վերադարձ (հարվածներ< 15); } }

Եվ այժմ երկու խաղացողների թել են մտնում ասպարեզ: Եկեք նրանց, առանց ավելորդ խոսքի, անվանենք Պինգ և Պոնգ.

PingPongGame (PingPongThread player1 = new PingPongThread ("Ping"); PingPongThread player2 = new PingPongThread ("Pong"); Ball ball; PingPongGame () (ball = Ball.getBall ();) void startGame () նետում է Player1 .start (); player2.start ();))

«Մարդկանց լիարժեք մարզադաշտ - հանդիպումը սկսելու ժամանակը»: Մենք պաշտոնապես կհայտարարենք հանդիպման բացման մասին `դիմումի հիմնական դասում.

Հանրային դասի PingPong (հանրային ստատիկ դատարկ հիմնական (լարային args) նետում է InterruptException (PingPongGame խաղ = նոր PingPongGame (); game.startGame ();)))

Ինչպես տեսնում եք, այստեղ կատաղի ոչինչ չկա: Սա առայժմ ընդամենը բազմաթեմատիկայի ներածություն է, բայց դուք արդեն գիտեք, թե ինչպես է այն աշխատում, և կարող եք փորձարկել ՝ սահմանափակեք խաղի տևողությունը ոչ թե հարվածների քանակով, այլ օրինակ ՝ ժամանակով: Մենք ավելի ուշ կանդրադառնանք բազմալեզու թեմային `մենք կանդրադառնանք java.util.concurrent փաթեթին, Akka գրադարանին և անկայուն մեխանիզմին: Եկեք խոսենք նաև Python- ում բազմալեզու նյութեր ներդնելու մասին:

Բազմաշերտ ծրագրավորումը սկզբունքորեն չի տարբերվում իրադարձությունների վրա հիմնված գրաֆիկական միջերեսներ գրելուց կամ նույնիսկ պարզ հաջորդական ծրագրեր գրելուց: Այստեղ կիրառվում են ծածկապատումը, մտահոգությունների տարանջատումը, չամրացված կապը կարգավորող բոլոր կարևոր կանոնները: Բայց շատ ծրագրավորողներ դժվարանում են գրել բազմալեզու ծրագրեր հենց այն պատճառով, որ անտեսում են այս կանոնները: Փոխարենը, նրանք փորձում են գործնականում կիրառել թելերի և համաժամացման պարզունակների մասին շատ ավելի քիչ կարևոր գիտելիքները, որոնք քաղված են սկսնակների համար բազմալեզու ծրագրավորման տեքստերից:

Այսպիսով, որոնք են այս կանոնները

Մեկ այլ ծրագրավորող, որը կանգնած է խնդրի առջև, կարծում է. Իսկ այժմ նա արդեն երկու խնդիր ունի ՝ Jamեյմի awավինսկին:

Մեկ այլ ծրագրավորող, որը բախվել է խնդրի հետ, մտածում է. Եվ հիմա նա ունի տասը խնդիր `Բիլ Շինդլեր:

Չափից շատ ծրագրավորողներ, ովքեր պարտավորվում են գրել բազմաթել կոդ, ընկնում են ծուղակը, ինչպես Գյոթեի բալլադի հերոսը » Կախարդի աշակերտը". Րագրավորողը կսովորի, թե ինչպես ստեղծել մի շարք թելեր, որոնք, սկզբունքորեն, աշխատում են, բայց վաղ թե ուշ նրանք դուրս են գալիս վերահսկողությունից, և ծրագրավորողը չգիտի, թե ինչ անել:

Բայց ի տարբերություն կախարդ-կիսատ թողածի, դժբախտ ծրագրավորողը չի կարող հույս ունենալ հզոր կախարդի ժամանման վրա, ով կշարժի իր գավազանն ու կվերականգնի կարգուկանոնը: Փոխարենը, ծրագրավորողը գնում է ամենաանհամար հնարքների ՝ փորձելով գլուխ հանել անընդհատ առաջացող խնդիրներից: Արդյունքը միշտ նույնն է. Ստացվում է չափազանց բարդ, սահմանափակ, փխրուն և անվստահելի ծրագիր: Այն ունի փակուղու մշտական ​​սպառնալիք և վատ բազմաթել ծածկագրին բնորոշ այլ վտանգներ: Էլ չեմ խոսում անբացատրելի վթարների, վատ աշխատանքի, թերի կամ սխալ աշխատանքի արդյունքների մասին:

Դուք երևի մտածել եք. Ինչու՞ է դա տեղի ունենում: Սովորական թյուրըմբռնումն այն է. «Բազմաշերտ ծրագրավորումը շատ դժվար է»: Բայց դա այդպես չէ: Եթե ​​բազմաթելային ծրագիրն անվստահելի է, ապա այն սովորաբար ձախողվում է նույն պատճառներով, ինչ ցածրորակ միայնակ թելերով ծրագիրը: Պարզապես ծրագրավորողը չի հետևում զարգացման հիմնարար, հայտնի և ապացուցված մեթոդներին: Բազմաշերտ ծրագրերը միայն ավելի բարդ են թվում, որովհետև որքան շատ զուգահեռ թելեր սխալ գնան, այնքան ավելի խառնաշփոթ են ստեղծում դրանք և շատ ավելի արագ, քան մեկ թելը:

«Բազմաթելային ծրագրավորման բարդության» մասին թյուրըմբռնումը լայն տարածում է գտել այն մշակողների շնորհիվ, ովքեր մասնագիտորեն զարգացել են մեկ շարանի ծածկագիր գրելիս, առաջին անգամ հանդիպել են բազմաթելերի և չեն հաղթահարել այն: Բայց իրենց կողմնակալություններն ու աշխատանքի սովորությունները վերանայելու փոխարեն նրանք համառորեն ամրագրում են այն փաստը, որ ոչ մի կերպ չեն ցանկանում աշխատել: Անհիմն ծրագրային ապահովման և բաց թողնված ժամկետների համար արդարացումներ գտնելով ՝ այս մարդիկ կրկնում են նույնը. «Բազմաթել ծրագրավորումը շատ դժվար է»:

Խնդրում ենք նկատի ունենալ, որ վերևում ես խոսում եմ տիպիկ ծրագրերի մասին, որոնք օգտագործում են բազմաթել: Իրոք, կան բարդ բազմաթել սցենարներ, ինչպես նաև բարդ միաթել սցենարներ: Բայց դրանք սովորական չեն: Որպես կանոն, գործնականում ծրագրավորողից ոչ մի գերբնական բան չի պահանջվում: Մենք տեղափոխում ենք տվյալները, փոխակերպում դրանք, ժամանակ առ ժամանակ կատարում ենք որոշ հաշվարկներ և, վերջապես, պահում ենք տեղեկատվությունը տվյալների բազայում կամ ցուցադրում էկրանին:

Ոչ մի դժվար բան չկա միջին թել ունեցող միջին ծրագիրը կատարելագործելու և այն բազմաթել դարձնելու մեջ: Առնվազն չպետք է լինի: Դժվարությունները ծագում են երկու պատճառով.

  • ծրագրավորողները չգիտեն, թե ինչպես կիրառել զարգացման պարզ, հայտնի ապացուցված մեթոդներ.
  • Բազմաթելավոր ծրագրավորման վերաբերյալ գրքերում ներկայացված տեղեկատվության մեծ մասը տեխնիկապես ճիշտ է, բայց լիովին չկիրառելի `կիրառական խնդիրների լուծման համար:

Programրագրավորման ամենակարևոր հասկացությունները համընդհանուր են: Դրանք հավասարապես կիրառելի են միայնակ և բազմաթել ծրագրերի դեպքում: Հոսքերի հորձանուտում խեղդվող ծրագրավորողները պարզապես չեն սովորել կարևոր դասեր, երբ նրանք յուրացրել են մեկ թելերով ծածկագիրը: Ես կարող եմ դա ասել, քանի որ նման ծրագրավորողները նույն հիմնարար սխալներն են թույլ տալիս բազմաթել և միաշարք ծրագրերում:

Perhapsրագրավորման պատմության վաթսուն տարվա ընթացքում, թերևս, ամենակարևոր դասը հետևյալն է. գլոբալ փոփոխական վիճակ- չար... Իսկական չարիք: Progրագրերը, որոնք հիմնված են գլոբալ փոփոխական վիճակի վրա, համեմատաբար դժվար է տրամաբանել և ընդհանրապես անվստահելի են, քանի որ վիճակը փոխելու չափազանց շատ եղանակներ կան: Եղել են բազմաթիվ ուսումնասիրություններ, որոնք հաստատում են այս ընդհանուր սկզբունքը, կան անհամար դիզայնի օրինաչափություններ, որոնց հիմնական նպատակն է տվյալների թաքցնելու այս կամ այն ​​կերպ իրականացումը: Ձեր ծրագրերն ավելի կանխատեսելի դարձնելու համար փորձեք հնարավորինս վերացնել փոփոխվող վիճակը:

Մեկ շարանի սերիալային ծրագրում տվյալների կոռուպցիայի հավանականությունը ուղիղ համեմատական ​​է այն բաղադրիչների թվին, որոնք կարող են փոփոխել տվյալները:

Որպես կանոն, հնարավոր չէ ամբողջովին ազատվել գլոբալ վիճակից, բայց մշակողը իր զինանոցում ունի շատ արդյունավետ գործիքներ, որոնք թույլ են տալիս խստորեն վերահսկել, թե ծրագրի որ բաղադրիչները կարող են փոխել վիճակը: Բացի այդ, մենք սովորեցինք, թե ինչպես կարելի է ստեղծել սահմանափակող API շերտեր պարզունակ տվյալների կառուցվածքների շուրջ: Հետևաբար, մենք լավ վերահսկողություն ունենք, թե ինչպես են փոխվում այս տվյալների կառուցվածքները:

Գլոբալ փոփոխական վիճակի խնդիրները աստիճանաբար ի հայտ եկան 80-ականների վերջին և 90-ականների սկզբին `իրադարձությունների վրա հիմնված ծրագրավորման տարածմամբ: Longerրագրերն այլևս «սկզբից» չէին սկսվում կամ «մինչև վերջ» կատարում էին կատարման մեկ, կանխատեսելի ուղի: Modernամանակակից ծրագրերն ունեն նախնական վիճակ, որից դուրս գալուց հետո դրանցում տեղի են ունենում իրադարձություններ `անկանխատեսելի կարգով, փոփոխական ժամանակային ընդմիջումներով: Կոդը մնում է միայնակ, բայց արդեն դառնում է ասինխրոն: Տվյալների կոռուպցիայի հավանականությունը մեծանում է հենց այն պատճառով, որ իրադարձությունների առաջացման կարգը շատ կարևոր է: Այսպիսի իրավիճակները բավականին տարածված են. Եթե B իրադարձությունը տեղի է ունենում A իրադարձությունից հետո, ապա ամեն ինչ լավ է աշխատում: Բայց եթե A իրադարձությունը տեղի է ունենում B իրադարձությունից հետո, և C իրադարձությունը ժամանակ ունի միջամտելու նրանց միջև, ապա տվյալները կարող են աղավաղվել անճանաչելիորեն:

Եթե ​​զուգահեռ հոսքեր են ներգրավվում, խնդիրն ավելի է սրվում, քանի որ մի քանի մեթոդներ կարող են միաժամանակ գործել գլոբալ վիճակի վրա: Անհնար է դառնում դատել, թե ինչպես է փոխվում գլոբալ պետությունը: Մենք արդեն խոսում ենք ոչ միայն այն մասին, որ իրադարձությունները կարող են տեղի ունենալ անկանխատեսելի կարգով, այլև այն մասին, որ կատարման մի քանի թելերի վիճակը կարող է թարմացվել: միաժամանակ... Ասինխրոն ծրագրավորման միջոցով դուք կարող եք, առնվազն, ապահովել, որ որոշակի իրադարձություն չի կարող տեղի ունենալ նախքան մեկ այլ իրադարձության մշակումն ավարտելը: Այսինքն ՝ կարելի է վստահաբար ասել, թե ինչպիսին կլինի գլոբալ վիճակը որոշակի իրադարձության մշակման ավարտին: Բազմաշերտ ծածկագրում, որպես կանոն, անհնար է ասել, թե որ իրադարձություններն են զուգահեռաբար տեղի ունենալու, ուստի անհնար է ժամանակի ցանկացած պահի վստահորեն նկարագրել գլոբալ վիճակը:

Գլոբալ փոփոխական վիճակով լայնածավալ բազմալեզու ծրագիրը Հայզենբերգի անորոշության սկզբունքի իմ իմացած ամենախոսուն օրինակներից մեկն է: Անհնար է ստուգել ծրագրի վիճակը ՝ առանց դրա վարքագիծը փոխելու:

Երբ սկսում եմ գլոբալ փոփոխվող վիճակի վերաբերյալ մեկ այլ ֆիլիպպիկ (էությունը նկարագրված է նախորդ մի քանի պարբերություններում), ծրագրավորողները պտտվում են աչքերով և վստահեցնում ինձ, որ նրանք վաղուց գիտեն այս ամենը: Բայց եթե դուք դա գիտեք, ինչու՞ չեք կարող ձեր կոդից ասել: Progրագրերը հագեցած են գլոբալ փոփոխական վիճակով, և ծրագրավորողները զարմանում են, թե ինչու կոդը չի աշխատում:

Sարմանալի չէ, որ բազմաթել ծրագրավորման ամենակարևոր աշխատանքը տեղի է ունենում նախագծման փուլում: Պահանջվում է հստակ սահմանել, թե ինչ պետք է անի ծրագիրը, մշակել անկախ մոդուլներ ՝ բոլոր գործառույթները կատարելու համար, մանրամասն նկարագրել, թե որ տվյալ մոդուլի համար ինչ տվյալներ են անհրաժեշտ, և որոշել մոդուլների միջև տեղեկատվության փոխանակման եղանակները ( Այո, մի մոռացեք պատրաստել գեղեցիկ շապիկներ նախագծում ներգրավված բոլորի համար: Առաջին բանը.- մոտ խմբ. բնօրինակով): Այս գործընթացը սկզբունքորեն չի տարբերվում մեկ շարանի ծրագրի նախագծումից: Հաջողության գրավականը, ինչպես և մեկ թելերով ծածկագիրը, մոդուլների միջև փոխազդեցության սահմանափակումն է: Եթե ​​կարողանաք ազատվել ընդհանուր փոփոխական վիճակից, տվյալների փոխանակման խնդիրները պարզապես չեն առաջանա:

Ինչ -որ մեկը կարող է պնդել, որ երբեմն ժամանակ չի մնում ծրագրի այնպիսի նուրբ ձևավորման համար, որը հնարավոր կդարձնի առանց գլոբալ պետության: Ես հավատում եմ, որ դրա վրա հնարավոր է և անհրաժեշտ է ժամանակ հատկացնել: Ոչինչ չի ազդում բազմալեզու ծրագրերի վրա այնքան կործանարար, որքան գլոբալ փոփոխական վիճակին դիմակայելու փորձերը: Որքան ավելի մանրամասն պետք է կառավարեք, այնքան ավելի հավանական է, որ ձեր ծրագիրը գագաթնակետին հասնի և վթարի ենթարկվի:

Իրատեսական ծրագրերում պետք է լինի որոշակի ընդհանուր վիճակ, որը կարող է փոխվել: Եվ հենց այստեղ է, որ ծրագրավորողների մեծ մասը սկսում է խնդիրներ ունենալ: Րագրավորողը տեսնում է, որ այստեղ պահանջվում է ընդհանուր վիճակ, դիմում է բազմաթել զինանոցին և այնտեղից վերցնում է ամենապարզ գործիքը ՝ ունիվերսալ կողպեքը (կրիտիկական հատված, մուտեքս կամ ինչպես են այն անվանում): Նրանք կարծես հավատում են, որ փոխադարձ բացառումը կլուծի տվյալների փոխանակման բոլոր խնդիրները:

Խնդիրների թիվը, որոնք կարող են ծագել նման մեկ կողպեքի դեպքում, ցնցող է: Պետք է հաշվի առնել մրցավազքի պայմանները, չափազանց ընդլայնված արգելափակման հետ կապված խնդիրները և բաշխման արդարության խնդիրները ընդամենը մի քանի օրինակ են: Եթե ​​ունեք բազմաթիվ կողպեքներ, հատկապես եթե դրանք ներկված են, ապա ձեզ հարկավոր է նաև միջոցներ ձեռնարկել փակուղու, դինամիկ փակուղու, հերթերի արգելափակման և համաժամանակության հետ կապված այլ սպառնալիքների դեմ: Բացի այդ, առկա են միայնակ արգելափակման բնորոշ խնդիրներ:
Երբ ես գրում կամ վերանայում եմ կոդը, ես ունեմ գրեթե անսխալական երկաթե կանոն. եթե դու կողպեք ես արել, ուրեմն կարծես ինչ -որ տեղ սխալվել ես.

Այս հայտարարությունը կարելի է մեկնաբանել երկու եղանակով.

  1. Եթե ​​ձեզ անհրաժեշտ է կողպում, ապա հավանաբար ունեք գլոբալ փոփոխական վիճակ, որը ցանկանում եք պաշտպանել միաժամանակյա թարմացումներից: Գլոբալ փոփոխվող վիճակի առկայությունը թերություն է կիրառման նախագծման փուլում: Վերանայել և վերափոխել:
  2. Կողպեքների ճիշտ օգտագործումը հեշտ չէ, և կարող է աներևակայելի դժվար լինել կողպման հետ կապված սխալների տեղայնացումը: Շատ հավանական է, որ կողպեքը սխալ օգտագործեք: Եթե ​​ես տեսնում եմ կողպեք, և ծրագիրը իրեն պահում է անսովոր ձևով, ապա առաջին բանը, որ ես անում եմ, ստուգել այն ծածկագիրը, որը կախված է կողպումից: Եվ ես սովորաբար դրա մեջ խնդիրներ եմ գտնում:

Այս երկու մեկնաբանություններն էլ ճիշտ են:

Բազմաշերտ ծածկագիր գրելը հեշտ է: Բայց շատ, շատ դժվար է համաժամացման պարզունակները ճիշտ օգտագործել: Գուցե դուք իրավասու չեք նույնիսկ մեկ կողպեքը ճիշտ օգտագործելու համար: Ի վերջո, կողպեքները և այլ համաժամացման պարզունակները կառուցվածքներ են, որոնք տեղադրված են ամբողջ համակարգի մակարդակով: Մարդիկ, ովքեր ձեզանից շատ ավելի լավ են հասկանում զուգահեռ ծրագրավորումը, օգտագործում են այս պարզունակները տվյալների միաժամանակյա կառուցվածքներ և բարձր մակարդակի համաժամացման կառուցվածքներ կառուցելու համար: Եվ դուք և ես, սովորական ծրագրավորողներ, պարզապես վերցնում ենք նման կոնստրուկցիաները և օգտագործում դրանք մեր ծածկագրում: Հավելվածի ծրագրավորողը չպետք է ավելի հաճախակի օգտագործի ցածր մակարդակի համաժամացման պարզունակներ, քան ուղղակի զանգեր է կատարում սարքի վարորդներին: Այսինքն ՝ գրեթե երբեք:

Տվյալների փոխանակման խնդիրները լուծելու համար կողպեքներ օգտագործելը նման է հեղուկ թթվածնով կրակ մարելուն: Հրդեհի նման նման խնդիրներն ավելի հեշտ է կանխել, քան շտկել: Եթե ​​դուք ազատվեք ընդհանուր վիճակից, ապա պետք չէ չարաշահել նաև համաժամացման պարզունակները:

Այն, ինչ դուք գիտեք բազմատեքստերի մասին, անտեղի է

Սկսնակների համար նախատեսված բազմալեզու ձեռնարկներում դուք կսովորեք, թե ինչ են թելերը: Այնուհետև հեղինակը կսկսի դիտարկել տարբեր եղանակներ, որոնցով այս թեմաները կարող են զուգահեռ աշխատել, օրինակ ՝ խոսել կողպեքների և սեմալիստների միջոցով ընդհանուր տվյալների հասանելիության վերահսկման մասին, անդրադառնալ իրադարձությունների հետ աշխատելիս ինչ կարող է տեղի ունենալ: Մանրակրկիտ կերպով կդիտարկի վիճակի փոփոխականները, հիշողության խոչընդոտները, կրիտիկական հատվածները, մուլտեքսները, անկայուն դաշտերը և ատոմային գործողությունները: Կքննարկվեն այն օրինակները, թե ինչպես օգտագործել այս ցածր մակարդակի կոնստրուկցիաները բոլոր տեսակի համակարգային գործողություններ կատարելու համար: Այս նյութը կիսով չափ կարդալուց հետո ծրագրավորողը որոշում է, որ արդեն բավականաչափ գիտի այս բոլոր պարզունակների և դրանց օգտագործման մասին: Ի վերջո, եթե ես գիտեմ, թե ինչպես է այս բանը գործում համակարգի մակարդակում, ես կարող եմ դա նույն կերպ կիրառել կիրառման մակարդակում: Այո?

Պատկերացրեք, որ դեռահասը պատմում է, թե ինչպես ինքնուրույն հավաքել ներքին այրման շարժիչը: Հետո, առանց վարելու վարժությունների, նրան դնում եք մեքենայի ղեկին և ասում ՝ «Գնա՛»: Դեռահասը հասկանում է, թե ինչպես է աշխատում մեքենան, բայց գաղափար չունի, թե ինչպես A կետից հասնել դրա վրա գտնվող B կետին:

Հասկանալը, թե ինչպես են թելերն աշխատում համակարգի մակարդակում, սովորաբար ոչ մի կերպ չի օգնում կիրառման մակարդակում: Ես չեմ առաջարկում, որ ծրագրավորողները կարիք չունեն սովորելու այս բոլոր ցածր մակարդակի մանրամասներին: Պարզապես մի ակնկալեք, որ կկարողանաք կիրառել այս գիտելիքները անմիջապես բիզնես ծրագիր մշակելիս կամ մշակելիս:

Ներածական թեմայով գրականությունը (և հարակից ակադեմիական դասընթացները) չպետք է ուսումնասիրի նման ցածր մակարդակի կառույցներ: Դուք պետք է կենտրոնանաք խնդիրների ամենատարածված դասերի լուծման վրա և ծրագրավորողներին ցույց տաք, թե ինչպես են այդ խնդիրները լուծվում բարձր մակարդակի հնարավորությունների կիրառմամբ: Սկզբունքորեն, բիզնես ծրագրերի մեծ մասը չափազանց պարզ ծրագրեր են: Նրանք կարդում են մեկ կամ մի քանի մուտքային սարքերի տվյալներ, կատարում են այս տվյալների որոշակի բարդ մշակում (օրինակ ՝ ընթացքում նրանք պահանջում են ևս մի քանի տվյալներ), այնուհետև տալիս արդյունքները:

Հաճախ նման ծրագրերը հիանալի տեղավորվում են մատակարար-սպառող մոդելի մեջ, որը պահանջում է ընդամենը երեք թել.

  • մուտքային հոսքը կարդում է տվյալները և դրանք դնում մուտքի հերթում.
  • աշխատողի շարանը կարդում է գրառումները մուտքի հերթից, մշակում դրանք և արդյունքները դնում ելքային հերթում.
  • ելքային հոսքը կարդում է մուտքի ելքային հերթից գրառումները և պահում դրանք:

Այս երեք թելերն աշխատում են ինքնուրույն, նրանց միջև հաղորդակցությունը տեղի է ունենում հերթի մակարդակում:

Թեև տեխնիկապես այդ հերթերը կարելի է համարել որպես ընդհանուր վիճակի գոտիներ, գործնականում դրանք պարզապես հաղորդակցության ուղիներ են, որոնցում գործում է իրենց ներքին համաժամացումը: Հերթերն աջակցում են միանգամից շատ արտադրողների և սպառողների հետ աշխատելուն, դրանցում կարող եք զուգահեռ ավելացնել և հեռացնել իրեր:

Քանի որ մուտքի, մշակման և ելքի փուլերը մեկուսացված են միմյանցից, դրանց իրականացումը կարող է հեշտությամբ փոխվել ՝ առանց ազդելու ծրագրի մնացած մասի վրա: Քանի դեռ հերթի տվյալների տեսակը չի փոխվում, ձեր հայեցողությամբ կարող եք վերափոխել ծրագրի առանձին բաղադրիչները: Բացի այդ, քանի որ հերթում մասնակցում են մատակարարների և սպառողների կամայական թվաքանակ, դժվար չէ ավելացնել այլ արտադրողներ / սպառողներ: Մենք կարող ենք ունենալ մի քանի տասնյակ մուտքային հոսքեր, որոնք գրում են տեղեկատվություն նույն հերթում, կամ տասնյակ աշխատող թելեր, որոնք տեղեկատվություն են վերցնում մուտքի հերթից և մարսում տվյալները: Մեկ համակարգչի շրջանակներում նման մոդելը լավ կշռում է:

Ամենակարևորը `ժամանակակից ծրագրավորման լեզուներն ու գրադարանները շատ հեշտացնում են արտադրող-սպառող ծրագրերի ստեղծումը: .NET- ում դուք կգտնեք զուգահեռ հավաքածուներ և TPL Dataflow Library: Java- ն ունի Executor ծառայությունը, ինչպես նաև java.util.concurrent namespace- ի BlockingQueue և այլ դասեր: C ++ - ն ունի Boost թելերի գրադարան և Intel- ի Thread Building Blocks գրադարան: Microsoft- ի Visual Studio 2013 -ը ներկայացնում է ասինխրոն գործակալներ: Նմանատիպ գրադարաններ կան նաև Python, JavaScript, Ruby, PHP և, որքան տեղյակ եմ, շատ այլ լեզուներով: Դուք կարող եք ստեղծել արտադրող-սպառող ծրագիր ՝ օգտագործելով այս փաթեթներից որևէ մեկը ՝ առանց որևէ դիմելու կողպեքների, սեմալիստների, վիճակի փոփոխականների կամ համաժամացման այլ պարզունակների:

Այս գրադարաններում ազատորեն օգտագործվում են համաժամացման պրիմիտիվների լայն տեսականի: Սա լավ է: Այս բոլոր գրադարանները գրված են այն մարդկանց կողմից, ովքեր բազմաթելից անհամեմատ ավելի լավ են հասկանում, քան միջին ծրագրավորողը: Նման գրադարանի հետ աշխատելը գործնականում նույնն է, ինչ գործածության լեզվով գրադարան օգտագործելը: Սա կարելի է համեմատել ոչ թե հավաքման, այլ բարձր մակարդակի ծրագրավորմամբ:

Մատակարար-սպառող մոդելը բազմաթիվ օրինակներից միայն մեկն է: Վերոնշյալ գրադարանները պարունակում են դասեր, որոնք կարող են օգտագործվել թելերի նախագծման շատ սովորական ձևերի իրականացման համար `առանց ցածր մակարդակի մանրամասների մեջ մտնելու: Հնարավոր է ստեղծել լայնածավալ բազմաթելային ծրագրեր ՝ առանց անհանգստանալու, թե ինչպես են թելերը համակարգվում և համաժամացվում:

Աշխատեք գրադարանների հետ

Այսպիսով, բազմաթելային ծրագրերի ստեղծումը սկզբունքորեն չի տարբերվում մեկ շարանի համաժամանակյա ծրագրեր գրելուց: Փակման և տվյալների թաքցնելու կարևոր սկզբունքները համընդհանուր են և կարևորվում են միայն այն դեպքում, երբ ներգրավված են միաժամանակ մի քանի թելեր: Եթե ​​դուք անտեսում եք այս կարևոր ասպեկտները, ապա նույնիսկ ցածր մակարդակի թելերի վերաբերյալ ամենաընդգրկուն գիտելիքները չեն փրկի ձեզ:

Modernամանակակից ծրագրավորողները պետք է լուծեն բազմաթիվ խնդիրներ կիրառական ծրագրավորման մակարդակով, պատահում է, որ պարզապես ժամանակ չկա մտածելու այն մասին, թե ինչ է կատարվում համակարգի մակարդակով: Որքան ավելի բարդ են դառնում ծրագրերը, այնքան ավելի բարդ մանրամասները պետք է թաքնվեն API մակարդակների միջև: Մենք դա անում ենք ավելի քան մեկ տասնյակ տարի: Կարելի է պնդել, որ համակարգի բարդության ծրագրավորողից որակական թաքցնելը հիմնական պատճառն է, թե ինչու ծրագրավորողը կարողանում է գրել ժամանակակից ծրագրեր: Այդ դեպքում, արդյո՞ք մենք չենք թաքցնում համակարգի բարդությունը ՝ գործարկելով UI հաղորդագրության օղակը, կառուցելով ցածր մակարդակի հաղորդակցության արձանագրություններ և այլն:

Նմանատիպ իրավիճակ է բազմաթերթերի դեպքում: Բազմաբնույթ սցենարների մեծ մասը, որոնց կարող է հանդիպել միջին բիզնես ծրագրերի ծրագրավորողը, արդեն հայտնի են և լավ կիրառված են գրադարաններում: Գրադարանի գործառույթները մեծ աշխատանք են կատարում թաքցնելու զուգահեռականության ճնշող բարդությունը: Դուք պետք է սովորեք, թե ինչպես օգտագործել այս գրադարանները այնպես, ինչպես օգտագործում եք ինտերֆեյսի տարրերի գրադարաններ, հաղորդակցության արձանագրություններ և պարզապես աշխատող բազմաթիվ այլ գործիքներ: Leaveածր մակարդակի բազմատեքստը թողեք մասնագետներին `գրադարանների հեղինակներին, որոնք օգտագործվում են հավելվածների ստեղծման մեջ:

ԱԱ Այս հոդվածը նախատեսված չէ Python- ի փորձառու ընտելացնողների համար, որոնց համար օձերի այս գնդակը քանդելը մանկական խաղ է, այլ ավելի շուտ ՝ նոր կախվածություն ունեցող պիթոնի համար բազմաթելային հնարավորությունների մակերեսային ակնարկ:

Unfortunatelyավոք, ռուսերենում այնքան էլ շատ նյութ չկա Python- ում բազմալեզու թեմայով, և պիթոնիստները, ովքեր ոչինչ չէին լսել, օրինակ ՝ GIL- ի մասին, սկսեցին ինձ հանդիպել նախանձելի օրինաչափությամբ: Այս հոդվածում ես կփորձեմ նկարագրել բազմաթել պիթոնի ամենակարևոր առանձնահատկությունները, պատմել ձեզ, թե ինչ է GIL- ը և ինչպես ապրել դրա հետ (կամ առանց դրա) և շատ ավելին:


Python- ը հմայիչ ծրագրավորման լեզու է: Այն հիանալի կերպով համատեղում է ծրագրավորման բազմաթիվ հարացույցներ: Առաջադրանքների մեծ մասը, որոնց կարող է հանդիպել ծրագրավորողը, լուծվում են այստեղ հեշտ, էլեգանտ և հակիրճ: Բայց այս բոլոր խնդիրների դեպքում մեկ թելի լուծումը հաճախ բավարար է, և մեկ թելերով ծրագրերը սովորաբար կանխատեսելի են և հեշտ վրիպազերծելի: Նույնը չի կարելի ասել բազմալար և բազմամշակող ծրագրերի մասին:

Բազմաշերտ ծրագրեր


Python- ն ունի մոդուլթելում , և այն ունի այն ամենը, ինչ ձեզ հարկավոր է բազմաթել ծրագրավորման համար. կան տարբեր տեսակի կողպեքներ, սեմալիստ և իրադարձությունների մեխանիզմ: Մեկ բառով `այն ամենը, ինչ անհրաժեշտ է բազմալեզու ծրագրերի ճնշող մեծամասնության համար: Ավելին, այս բոլոր գործիքների օգտագործումը բավականին պարզ է: Եկեք դիտարկենք ծրագրի օրինակ, որը սկսում է 2 թեմա: Մեկ շարանը գրում է տասը «0», մյուսը ՝ տասը «1», ևխստորեն իր հերթին:

ներմուծել թելեր

def գրող

i xrange- ում (10):

տպել x

Event_for_set.set ()

# սկզբնական իրադարձություն

e1 = անցում: Միջոցառում ()

e2 = անցում: Միջոցառում ()

# ինիտ թեմա

0, e1, e2))

1, e2, e1))

# սկսել թեմա

t1.start ()

t2.start ()

t1.միանալ ()

t2. միանալ ()


Կախարդական կամ վուդու ծածկագիր չկա: Կոդը հստակ է և հետևողական: Ավելին, ինչպես տեսնում եք, մենք գործառույթից հոսք ենք ստեղծել: Սա շատ հարմար է փոքր առաջադրանքների համար: Այս ծածկագիրը նույնպես բավականին ճկուն է: Ենթադրենք, որ մենք ունենք երրորդ գործընթաց, որը գրում է «2», ապա ծածկագիրն այսպիսի տեսք կունենա.

ներմուծել թելեր

def գրող (x, event_for_wait, event_for_set):

i xrange- ում (10):

Event_for_wait.wait () # սպասել իրադարձությանը

Event_for_wait.clear () # մաքուր իրադարձություն ապագայի համար

տպել x

Event_for_set.set () # սահմանել իրադարձություն հարևան թելի համար

# սկզբնական իրադարձություն

e1 = անցում: Միջոցառում ()

e2 = անցում: Միջոցառում ()

e3 = անցում: Միջոցառում ()

# ինիտ թեմա

t1 = թել: Թել (նպատակ = գրող, args = ( 0, e1, e2))

t2 = թել: Թել (նպատակ = գրող, args = ( 1, e2, e3))

t3 = թել: Թել (նպատակ = գրող, args = ( 2, e3, e1))

# սկսել թեմա

t1.start ()

t2.start ()

t3. սկսել ()

e1.set () # նախաձեռնել առաջին իրադարձությունը

# միացրեք շարանը հիմնական շարանին

t1.միանալ ()

t2. միանալ ()

t3. միանալ ()


Մենք ավելացրեցինք նոր իրադարձություն, նոր թեմա և մի փոքր փոխեցինք այն պարամետրերը, որոնցով
հոսքերը սկսվում են (իհարկե, կարող եք գրել ավելի ընդհանուր լուծում ՝ օգտագործելով, օրինակ, MapReduce- ը, բայց դա դուրս է այս հոդվածի շրջանակներից):
Ինչպես տեսնում եք, կախարդություն դեռ չկա: Ամեն ինչ պարզ է և պարզ: Եկեք ավելի հեռուն գնանք:

Global Interpreter Lock


Թելեր օգտագործելու երկու ամենատարածված պատճառ կա. Առաջինը `բարձրացնել ժամանակակից պրոցեսորների բազմակողմանի ճարտարապետության օգտագործման արդյունավետությունը, և, հետևաբար, ծրագրի կատարումը.
երկրորդ ՝ եթե մենք պետք է ծրագրի տրամաբանությունը բաժանենք զուգահեռ, ամբողջությամբ կամ մասամբ ասինխրոն բաժինների (օրինակ ՝ միաժամանակ մի քանի սերվեր պինգ անելու հնարավորություն ունենալու համար):

Առաջին դեպքում մենք բախվում ենք Python- ի (ավելի ճիշտ ՝ դրա հիմնական CPython- ի իրականացման) այնպիսի սահմանափակման, ինչպիսին է Global Interpreter Lock- ը (կամ կարճ GIL): GIL- ի հասկացությունն այն է, որ միայն մեկ թելը կարող է միաժամանակ մշակվել պրոցեսորի կողմից: Դա արվում է այնպես, որ առանձին փոփոխականների համար թելերի միջև պայքար չլինի: Գործարկվող շարանը մուտք է ստանում ամբողջ միջավայր: Թղթերի ներդրման այս առանձնահատկությունը Python- ում մեծապես հեշտացնում է աշխատանքը թելերով և տալիս թելի որոշակի անվտանգություն:

Բայց կա մի նուրբ կետ. Կարող է թվալ, որ բազմաթել կիրառումը կաշխատի ճիշտ նույնքան ժամանակ, որքան մեկ թել կիրառողը նույնը կամ CPU- ի յուրաքանչյուր թելի կատարման ժամանակի գումարը: Բայց այստեղ մեզ սպասում է մեկ տհաճ էֆեկտ: Հաշվի առեք ծրագիրը.

բաց ("test1.txt", "w") որպես fout:

i xrange- ում (1000000):

տպել >> fout, 1


Այս ծրագիրը պարզապես գրում է մեկ տող «1» ֆայլին և դա անում է իմ համակարգչում 35 0.35 վայրկյանում:

Մտածեք մեկ այլ ծրագրի մասին.

threading ներմուծումից Thread

def գրող (ֆայլի անուն, n):

բաց (ֆայլի անունը, "w") որպես fout:

համար i xrange (n):

տպել >> fout, 1

t1 = Թեման (թիրախ = գրող, args = ("test2.txt", 500000,))

t2 = Թեման (թիրախ = գրող, args = ("test3.txt", 500000,))

t1.start ()

t2.start ()

t1.միանալ ()

t2. միանալ ()


Այս ծրագիրը ստեղծում է 2 թեմա: Յուրաքանչյուր թեմայում այն ​​գրում է առանձին ֆայլ ՝ կես միլիոն տող «1»: Փաստորեն, աշխատանքի ծավալը նույնն է, ինչ նախորդ ծրագրում: Բայց ժամանակի ընթացքում այստեղ հետաքրքիր ազդեցություն է ձեռք բերվում: Canրագիրը կարող է աշխատել 0.7 վայրկյանից մինչև 7 վայրկյան: Ինչու՞ է դա տեղի ունենում:

Դա պայմանավորված է նրանով, որ երբ շարանը կարիք չունի պրոցեսորի ռեսուրսի, այն թողարկում է GIL- ը, և այս պահին այն կարող է փորձել ստանալ այն, և մեկ այլ թեմա, և նաև հիմնական շարանը: Միևնույն ժամանակ, օպերացիոն համակարգը, իմանալով, որ շատ միջուկներ կան, կարող է սրել ամեն ինչ ՝ փորձելով թելեր տարածել միջուկների միջև:

UPD. Այս պահին, Python 3.2 -ում, կա GIL- ի կատարելագործված կատարում, որում այս խնդիրը մասամբ լուծվում է, մասնավորապես, այն պատճառով, որ յուրաքանչյուր թել, վերահսկողությունը կորցնելուց հետո, սպասում է կարճ ժամանակ առաջ կարող է կրկին գրավել GIL- ը (անգլերեն լեզվով լավ ներկայացում կա)

«Այսպիսով, չե՞ք կարող արդյունավետ բազմալեզու ծրագրեր գրել Python- ում», - հարցնում եք դուք: Ոչ, իհարկե, կա ելք, և նույնիսկ մի քանիսը:

Բազմամշակման ծրագրեր


Նախորդ պարբերությունում նկարագրված խնդիրը որոշ իմաստով լուծելու համար Python- ն ունի մոդուլենթամշակման ... Մենք կարող ենք գրել ծրագիր, որը մենք ցանկանում ենք կատարել զուգահեռ շարանում (իրականում արդեն գործընթաց է): Եվ գործարկեք այն մեկ կամ մի քանի թեմայում մեկ այլ ծրագրում: Սա իսկապես կարագացնի մեր ծրագիրը, քանի որ GIL գործարկիչում ստեղծված թելերը չեն հավաքվում, այլ սպասում են միայն ընթացող գործընթացի ավարտին: Այնուամենայնիվ, այս մեթոդը շատ խնդիրներ ունի: Հիմնական խնդիրն այն է, որ դժվարանում է տվյալների փոխանցումը գործընթացների միջև: Դուք ստիպված կլինեք ինչ -որ կերպ սերիալացնել օբյեկտները, կապ հաստատել PIPE- ի կամ այլ գործիքների միջոցով, բայց այս ամենն անխուսափելիորեն գլխավերևում է, և ծածկագիրը դառնում է դժվար հասկանալի:

Մեկ այլ մոտեցում կարող է օգնել մեզ այստեղ: Python- ն ունի բազմամշակման մոդուլ ... Ֆունկցիոնալության առումով այս մոդուլը նման էթելում ... Օրինակ, գործընթացները կարող են նույն կերպ ստեղծվել սովորական գործառույթներից: Գործընթացների հետ աշխատելու մեթոդները գրեթե նույնն են, ինչ թելերի մոդուլից թելերի դեպքում: Բայց գործընթացների համաժամացման և տվյալների փոխանակման համար ընդունված է օգտագործել այլ գործիքներ: Խոսքը հերթերի (հերթ) եւ խողովակների (խողովակ) մասին է: Այնուամենայնիվ, կողպեքների, իրադարձությունների և սեմալիստների անալոգները, որոնք գտնվում էին թելերում, նույնպես այստեղ են:

Բացի այդ, բազմամշակման մոդուլն ունի ընդհանուր հիշողության հետ աշխատելու մեխանիզմ: Դրա համար մոդուլն ունի փոփոխականի (արժեք) և զանգվածի (զանգված) դասեր, որոնք կարող են «կիսվել» գործընթացների միջև: Համօգտագործվող փոփոխականների հետ աշխատելու հարմարության համար կարող եք օգտագործել մենեջերի դասերը: Դրանք ավելի ճկուն են և ավելի հեշտ օգտագործման համար, բայց ավելի դանդաղ: Հարկ է նշել, որ հիանալի հնարավորություն է ստեղծվում ctypes մոդուլից սովորական տեսակներ պատրաստել ՝ օգտագործելով multiprocessing.sharedctypes մոդուլը:

Նաև բազմամշակման մոդուլում կա գործընթացի լողավազաններ ստեղծելու մեխանիզմ: Այս մեխանիզմը շատ հարմար է օգտագործել Master-Worker օրինակը կամ զուգահեռ քարտեզը (որն ինչ-որ իմաստով Master-Worker- ի հատուկ դեպքն է) իրականացնելու համար:

Բազմամշակման մոդուլի հետ աշխատելու հիմնական խնդիրներից հարկ է նշել այս մոդուլի հարթակային հարաբերական կախվածությունը: Քանի որ գործընթացների հետ աշխատանքը տարբեր կերպ է կազմակերպվում տարբեր օպերացիոն համակարգերում, որոշ սահմանափակումներ են դրվում կոդի վրա: Օրինակ, Windows- ը չունի պատառաքաղի մեխանիզմ, ուստի գործընթացի տարանջատման կետը պետք է փաթաթված լինի.

եթե __name__ == "__ հիմնական__":


Այնուամենայնիվ, այս դիզայնն արդեն լավ ձև է:

Էլ ինչ...


Գոյություն ունեն այլ գրադարաններ և մոտեցումներ Python- ում զուգահեռ ծրագրեր գրելու համար: Օրինակ, կարող եք օգտագործել Hadoop + Python կամ Python MPI տարբեր իրականացումներ (pyMPI, mpi4py): Դուք նույնիսկ կարող եք օգտագործել գոյություն ունեցող C ++ կամ Fortran գրադարանների փաթաթիչներ: Այստեղ կարելի է նշել այնպիսի շրջանակներ / գրադարաններ, ինչպիսիք են Pyro- ն, Twisted- ը, Tornado- ն և շատ ուրիշներ: Բայց այս ամենն արդեն դուրս է այս հոդվածի շրջանակներից:

Եթե ​​ձեզ դուր եկավ իմ ոճը, ապա հաջորդ հոդվածում ես կփորձեմ ձեզ ասել, թե ինչպես գրել պարզ թարգմանիչներ PLY- ում և ինչի համար կարող են դրանք օգտագործվել:

Գլուխ 10:

Բազմաշերտ ծրագրեր

Operatingամանակակից օպերացիոն համակարգերում բազմակի առաջադրանքները ընդունված են [ Մինչև Apple OS X- ի գալուստը, Macintosh համակարգիչներում չկային ժամանակակից բազմաֆունկցիոնալ օպերացիոն համակարգեր: Շատ դժվար է լիարժեք բազմակողմանի օպերացիոն համակարգ նախագծելը, այնպես որ OS X- ը պետք է հիմնված լիներ Unix- ի վրա:]: Օգտվողն ակնկալում է, որ երբ միաժամանակ գործարկվեն տեքստային խմբագրիչը և փոստի հաճախորդը, այդ ծրագրերը չեն բախվի, և էլ. Փոստ ստանալիս խմբագիրը չի դադարի աշխատել: Երբ մի քանի ծրագիր գործարկվում է միաժամանակ, օպերացիոն համակարգը արագ անցնում է ծրագրերի միջև ՝ իրենց հերթին ապահովելով պրոցեսորով (եթե, իհարկե, համակարգչում տեղադրված չեն մի քանի պրոցեսորներ): Որպես արդյունք, պատրանքմիաժամանակ մի քանի ծրագրերի գործարկում, քանի որ նույնիսկ ամենալավ մեքենագրողը (և ամենաարագ ինտերնետ կապը) չի կարող հետևել ժամանակակից պրոցեսորին:

Multithreading- ը, ինչ -որ իմաստով, կարող է դիտվել որպես բազմակի առաջադրանքների հաջորդ մակարդակ. ծրագրեր,օպերացիոն համակարգը անցնում է նույն ծրագրի տարբեր մասերի միջև: Օրինակ, բազմաշերտ էլփոստի հաճախորդը թույլ է տալիս ստանալ նոր էլ.փոստեր ՝ նոր հաղորդագրություններ կարդալիս կամ կազմելիս: Մեր օրերում բազմատեքստը նույնպես ընդունված է համարվում շատ օգտվողների կողմից:

VB- ն երբեք չի ունեցել սովորական բազմալեզու աջակցություն: Trueիշտ է, դրա սորտերից մեկը հայտնվեց VB5- ում - համատեղ հոսքային մոդել(բնակարանների թելեր): Ինչպես կարճ ժամանակ անց կտեսնեք, համատեղ մոդելը ծրագրավորողին տալիս է բազմատեքստերի առավելություններից մի քանիսը, սակայն այն ամբողջությամբ չի օգտվում բոլոր հնարավորություններից: Վաղ թե ուշ դուք պետք է վերապատրաստման մեքենայից անցնեք իրականի, և VB .NET- ը դարձավ VB- ի առաջին տարբերակը `անվճար բազմաթելային մոդելի աջակցությամբ:

Այնուամենայնիվ, բազմատեքստը այն հատկանիշներից չէ, որոնք հեշտությամբ իրագործվում են ծրագրավորման լեզուներում և հեշտությամբ յուրացվում ծրագրավորողների կողմից: Ինչո՞ւ:

Քանի որ բազմաշերտ ծրագրերում կարող են առաջանալ շատ բարդ սխալներ, որոնք հայտնվում և անհետանում են անկանխատեսելիորեն (և նման սխալներն ամենադժվարը կարգաբերելն են):

Ազնվորեն նախազգուշացում. Բազմալեզու գրելը ծրագրավորման ամենադժվար ոլորտներից մեկն է: Ամենափոքր անուշադրությունը հանգեցնում է խուսափողական սխալների ի հայտ գալուն, որոնց ուղղումը տանում է աստղաբաշխական գումարներ: Այդ իսկ պատճառով այս գլուխը պարունակում է բազմաթիվ վատօրինակներ. մենք դրանք դիտավորյալ ենք գրել այնպես, որ ցույց տանք ընդհանուր սխալները: Սա բազմաթել ծրագրավորում սովորելու ամենաանվտանգ մոտեցումն է. Դուք պետք է կարողանաք նկատել հնարավոր խնդիրները, երբ ամեն ինչ առաջին հայացքից թվում է, թե լավ է, և իմանաք, թե ինչպես դրանք լուծել: Եթե ​​ցանկանում եք օգտագործել բազմաշերտ ծրագրավորման տեխնիկա, ապա առանց դրա չեք կարող:

Այս գլուխը ամուր հիմք կդնի հետագա անկախ աշխատանքի համար, բայց մենք չենք կարողանա նկարագրել բազմաթել ծրագրավորումը բոլոր խճճվածություններով. Միայն Threading անվան տարածքի դասերի վերաբերյալ տպագրված փաստաթղթերը տևում են ավելի քան 100 էջ: Եթե ​​ցանկանում եք տիրապետել բազմալեզու ծրագրավորման ավելի բարձր մակարդակի, դիմեք մասնագիտացված գրքերին:

Բայց անկախ նրանից, թե որքան վտանգավոր է բազմալեզու ծրագրավորումը, այն անփոխարինելի է որոշ խնդիրների մասնագիտական ​​լուծման համար: Եթե ​​ձեր ծրագրերում անհրաժեշտության դեպքում չեն օգտագործվում բազմաթելեր, օգտվողները շատ հիասթափված կլինեն և նախընտրեն այլ ապրանք: Օրինակ, Eudora էլեկտրոնային փոստի հանրաճանաչ ծրագրի միայն չորրորդ տարբերակում հայտնվեցին բազմալեզու հնարավորություններ, առանց որոնց անհնար է պատկերացնել էլեկտրոնային փոստի հետ աշխատելու որևէ ժամանակակից ծրագիր: Մինչև Eudora- ն ներկայացրեց բազմալեզու աջակցություն, շատ օգտվողներ (ներառյալ այս գրքի հեղինակներից մեկը) անցել էին այլ ապրանքների:

Ի վերջո, .NET- ում մեկ շարանի ծրագրեր պարզապես գոյություն չունեն: Ամեն ինչ.NET ծրագրերը բազմաթել են, քանի որ աղբահավաքը գործում է որպես ցածր առաջնահերթ ֆոնային գործընթաց: Ինչպես ցույց է տրված ստորև.

Ներկայացնում ենք բազմատեքստ

Յուրաքանչյուր ծրագիր աշխատում է որոշակի համատեքստ,նկարագրելով ծածկագրերի և տվյալների բաշխումը հիշողության մեջ: Համատեքստը պահպանելով ՝ ծրագրի հոսքի վիճակը փաստացի պահպանվում է, ինչը թույլ է տալիս հետագայում վերականգնել այն և շարունակել ծրագրի կատարումը:

Համատեքստը պահպանելը ժամանակի և հիշողության հետ կապված ծախսեր ունի: Օպերացիոն համակարգը հիշում է ծրագրի թելի վիճակը և կառավարումը փոխանցում է մեկ այլ թելի: Երբ ծրագիրը ցանկանում է շարունակել կասեցված շարանը կատարել, պահպանված համատեքստը պետք է վերականգնվի, ինչը նույնիսկ ավելի երկար է տևում: Հետևաբար, բազմատեքստը պետք է օգտագործվի միայն այն դեպքում, երբ օգուտները փոխհատուցեն բոլոր ծախսերը: Որոշ բնորոշ օրինակներ թվարկված են ստորև:

  • Theրագրի ֆունկցիոնալությունը հստակ և բնականաբար բաժանված է մի քանի տարասեռ գործողությունների, ինչպես օրինակ էլեկտրոնային փոստի ստացման և նոր հաղորդագրությունների պատրաստման օրինակով:
  • Theրագիրը կատարում է երկար և բարդ հաշվարկներ, և դուք չեք ցանկանում, որ գրաֆիկական ինտերֆեյսը արգելափակվի հաշվարկների տևողության ընթացքում:
  • Runsրագիրն աշխատում է բազմապրոցեսորային համակարգչով `օպերացիոն համակարգով, որն ապահովում է բազմաթիվ պրոցեսորների օգտագործումը (քանի դեռ ակտիվ թելերի թիվը չի գերազանցում պրոցեսորների թիվը, զուգահեռ կատարումը գործնականում զերծ է թելերի փոխարկման հետ կապված ծախսերից):

Նախքան բազմաթել ծրագրերի մեխանիկան անցնելը, անհրաժեշտ է նշել մի հանգամանք, որը հաճախ շփոթություն է առաջացնում բազմաթել ծրագրավորման ոլորտում սկսնակների մոտ:

Procedureրագրի հոսքում կատարվում է ոչ թե օբյեկտ, այլ ընթացակարգ:

Դժվար է ասել, թե ինչ նկատի ունի «օբյեկտը կատարվում է» արտահայտությունը, բայց հեղինակներից մեկը հաճախ դասավանդում է բազմատեքստային ծրագրավորման սեմինարներ, և այս հարցը տրվում է ավելի հաճախ, քան մյուսները: Հավանաբար ինչ -որ մեկը կարծում է, որ ծրագրի թելի աշխատանքը սկսվում է դասի Նոր մեթոդի կանչով, որից հետո շարանը մշակում է համապատասխան օբյեկտին փոխանցված բոլոր հաղորդագրությունները: Նման ներկայացուցչություններ բացարձակապեսսխալվում են: Մեկ օբյեկտը կարող է պարունակել մի քանի թելեր, որոնք կատարում են տարբեր (և երբեմն նույնիսկ նույն) մեթոդներ, մինչդեռ օբյեկտի հաղորդագրությունները փոխանցվում և ստացվում են մի քանի տարբեր թելերով (ի դեպ, սա մեկն է այն պատճառներից մեկը, որը բարդացնում է բազմաթել ծրագրավորումը. ծրագիրը կարգաբերելու համար հարկավոր է պարզել, թե տվյալ պահին ո՞ր թելն է կատարում այս կամ այն ​​ընթացակարգը):

Քանի որ թելերը ստեղծվում են օբյեկտների մեթոդներից, օբյեկտը ինքնին սովորաբար ստեղծվում է թելի առաջ: Օբյեկտը հաջողությամբ ստեղծելուց հետո ծրագիրը ստեղծում է թել ՝ նրան փոխանցելով օբյեկտի մեթոդի հասցեն և միայն դրանից հետոհրաման է տալիս սկսել շարանի կատարումը: Գործընթացը, որի համար ստեղծվել է շարանը, ինչպես և բոլոր ընթացակարգերը, կարող է ստեղծել նոր օբյեկտներ, գործողություններ կատարել գոյություն ունեցող օբյեկտների վրա և կանչել այլ ընթացակարգեր և գործառույթներ, որոնք գտնվում են դրա շրջանակում:

Դասերի սովորական մեթոդները կարող են իրականացվել նաև ծրագրի թելերում: Այս դեպքում հիշեք նաև մեկ այլ կարևոր հանգամանք. Շարանը ավարտվում է այն ընթացակարգից դուրս գալով, որի համար այն ստեղծվել է: Flowրագրի հոսքի նորմալ ավարտը հնարավոր չէ, քանի դեռ ընթացակարգը չի ավարտվել:

Թելերը կարող են դադարել ոչ միայն բնական, այլև աննորմալ: Սա ընդհանրապես խորհուրդ չի տրվում: Լրացուցիչ տեղեկությունների համար տե՛ս հոսքերի դադարեցում և ընդհատում:

.Րագրի թելերի օգտագործման հետ կապված հիմնական .NET գործիքները կենտրոնացած են Threading անվանատարածքում: Հետևաբար, բազմալեզու ծրագրերի մեծ մասը պետք է սկսվի հետևյալ տողից.

Ներմուծման համակարգ. Թեմա

Անունային տարածքի ներմուծումը հեշտացնում է ձեր ծրագրի մուտքագրումը և հնարավորություն է տալիս IntelliSense տեխնոլոգիան:

Հոսքերի ուղղակի կապը ընթացակարգերի հետ հուշում է, որ այս նկարում, պատվիրակներ(տես գլուխ 6): Մասնավորապես, Threading անվան տարածքը ներառում է ThreadStart պատվիրակը, որը սովորաբար օգտագործվում է ծրագրի թելերը սկսելիս: Այս պատվիրակի օգտագործման շարահյուսությունն այսպիսին է.

Հանրային պատվիրակ ենթածրագրի մեկնարկ ()

ThreadStart- ի պատվիրակի հետ կանչված ծածկագիրը չպետք է պարամետրեր կամ վերադարձի արժեք ունենա, ուստի թելեր չեն կարող ստեղծվել գործառույթների համար (որոնք վերադարձնում են արժեքը) և պարամետրերով ընթացակարգերի համար: Հոսքից տեղեկատվություն փոխանցելու համար պետք է նաև այլընտրանքային միջոցներ փնտրել, քանի որ կատարված մեթոդները չեն վերադարձնում արժեքները և չեն կարող օգտագործել հղումը հղումով: Օրինակ, եթե ThreadMethod- ը գտնվում է WilluseThread դասում, ապա ThreadMethod- ը կարող է տեղեկատվություն փոխանցել ՝ փոփոխելով WillUseThread դասի օրինակների հատկությունները:

Դիմումի տիրույթներ

.NET թելերն աշխատում են, այսպես կոչված, կիրառական տիրույթներում, որոնք փաստաթղթերում սահմանվում են որպես «ավազատուփ, որում կիրառվում է ծրագիրը»: Դիմումի տիրույթը կարելի է համարել որպես Win32 գործընթացների թեթև տարբերակ; մեկ Win32 գործընթացը կարող է պարունակել բազմաթիվ կիրառական տիրույթներ: Դիմումի տիրույթների և գործընթացների միջև հիմնական տարբերությունն այն է, որ Win32 գործընթացն ունի իր հասցեի տարածքը (փաստաթղթերում կիրառման տիրույթները նույնպես համեմատվում են ֆիզիկական գործընթացի ներսում ընթացող տրամաբանական գործընթացների հետ): NET- ում բոլոր հիշողության կառավարումն իրականացվում է գործարկման ժամանակ, այնպես որ մի քանի կիրառական տիրույթներ կարող են գործարկվել մեկ Win32 գործընթացում: Այս սխեմայի առավելություններից մեկը ծրագրերի մասշտաբավորման բարելավված հնարավորություններն են: Դիմումի տիրույթների հետ աշխատելու գործիքները գտնվում են AppDomain դասում: Խորհուրդ ենք տալիս ուսումնասիրել այս դասի փաստաթղթերը: Նրա օգնությամբ դուք կարող եք տեղեկատվություն ստանալ այն միջավայրի մասին, որտեղ աշխատում է ձեր ծրագիրը: Մասնավորապես, .DET համակարգի դասերին արտացոլում կատարելիս օգտագործվում է AppDomain դասը: Հետևյալ ծրագիրը թվարկում է բեռնված հավաքույթները:

Ներմուծման համակարգ. Արտացոլում

Մոդուլի մոդուլ

Ենթա հիմնական ()

Dim theDomain- ը որպես AppDomain

theDomain = AppDomain.CurrentDomain

Dim Assemblies () As

Assemblies = theDomain.GetAssemblies

Dim anAssemblyxAs

Anողովներում յուրաքանչյուր Անավարտի համար

Console.WriteLinetanAssembly.Full Name) Հաջորդը

Console.ReadLine ()

Վերջ ենթ

Ավարտի մոդուլ

Հոսքերի ստեղծում

Սկսենք տարրական օրինակով: Ենթադրենք, դուք ցանկանում եք ընթացակարգ վարել առանձին թեմայում, որը նվազեցնում է հակաչափ արժեքը անվերջ հանգույցում: Ընթացակարգը սահմանվում է որպես դասի մի մաս.

Հանրային դաս WillUseThreads

Public Sub SubtractFromCounter ()

Dim հաշվարկը որպես ամբողջ թիվ

Do while True հաշվել - = 1

Console.WriteLlne ("Ես այլ թեմայում եմ և հաշվիչ ="

& հաշվել)

Օղակ

Վերջ ենթ

Ավարտի դաս

Քանի որ Do loop պայմանը միշտ ճշմարիտ է, կարող եք մտածել, որ ոչինչ չի խանգարի SubtractFromCounter ընթացակարգին: Այնուամենայնիվ, բազմալեզու հավելվածում դա միշտ չէ, որ այդպես է:

Հետևյալ հատվածը ցույց է տալիս Sub Main ընթացակարգը, որը սկսում է շարանը և ներմուծման հրամանը.

Տարբերակ խիստ ներմուծման համակարգում: Թեմաների մոդուլի մոդուլ

Ենթա հիմնական ()

1 Dim myTest As New WillUseThreads ()

2 Dim bThreadStart As New ThreadStart (AddressOf _

myTest.SubtractFromCounter)

3 Dim bThread As New Thread (bThreadStart)

4 "bThread.Start ()

Dim i As Integer

5 Doշմարիտ եղիր

Console.WriteLine («Հիմնական թեմայում և հաշվում է» & i) i + = 1

Օղակ

Վերջ ենթ

Ավարտի մոդուլ

Եկեք հաջորդաբար նայենք ամենակարևոր կետերին: Նախ, Sub Man n ընթացակարգը միշտ գործում է հիմնական հոսք(հիմնական շարանը): .NET ծրագրերում միշտ գործում է առնվազն երկու թեմա ՝ հիմնական թելը և աղբահանության շարանը: Տող 1 -ը ստեղծում է թեստային դասի նոր օրինակ: 2 -րդ տողում մենք ստեղծում ենք ThreadStart պատվիրակ և փոխանցում SubtractFromCounter ընթացակարգի հասցեն 1 -ին տողում ստեղծված թեստային դասի օրինակին (այս ընթացակարգը կոչվում է առանց պարամետրերի): ԼավՆերմուծելով Threading անվան տարածքը ՝ երկար անունը կարող է բաց թողնվել: Նոր թեմայի օբյեկտը ստեղծվում է տողում 3: Թեմայի դասի կոնստրուկտոր կանչելիս նկատեք ThreadStart- ի պատվիրակի անցումը: Որոշ ծրագրավորողներ նախընտրում են այս երկու տողերը միացնել մեկ տրամաբանական գծի.

Dim bThread As New Thread (New ThreadStarttAddressOf _

myTest.SubtractFromCounter))

Վերջապես, 4 -րդ տողը «սկսում է» շարանը ՝ կանչելով ThreadStart- ի պատվիրակի համար ստեղծված Thread օրինակի Start մեթոդը: Այս մեթոդը կանչելով ՝ մենք օպերացիոն համակարգին ասում ենք, որ Subtract կարգը պետք է գործի առանձին թեմայով:

Նախորդ պարբերության «սկսվում» բառը փակված է չակերտների մեջ, քանի որ սա բազմաթել ծրագրավորման բազմաթիվ տարօրինակություններից մեկն է. Calling Start- ը իրականում չի սկսում շարանը: Այն միայն հրամայում է օպերացիոն համակարգին ժամանակացույց սահմանել նշված շարանը գործարկելու համար, սակայն ծրագրի վերահսկողությունից դուրս է ուղղակիորեն սկսելը: Դուք չեք կարողանա ինքնուրույն սկսել թելերի կատարումը, քանի որ օպերացիոն համակարգը միշտ վերահսկում է թելերի կատարումը: Հետագա բաժնում դուք կսովորեք, թե ինչպես օգտագործել առաջնահերթությունը, որպեսզի օպերացիոն համակարգը ավելի արագ սկսի ձեր շարանը:

Նկ. 10.1 -ը ցույց է տալիս, թե ինչ կարող է տեղի ունենալ ծրագիրը սկսելուց հետո, այնուհետև այն ընդհատել Ctrl + Break ստեղնով: Մեր դեպքում նոր թել սկսվեց միայն այն բանից հետո, երբ հիմնական շարանի հաշվիչը ավելացավ մինչև 341:

Բրինձ 10.1. Պարզ բազմաշերտ ծրագրակազմի գործարկման ժամանակ

Եթե ​​ծրագիրը գործի ավելի երկար ժամանակ, արդյունքը նման կլինի Նկարում պատկերվածին: 10.2. Մենք տեսնում ենք, որ դուընթացող թելի ավարտը կասեցվում է, և կառավարումը կրկին փոխանցվում է հիմնական թելին: Այս դեպքում կա դրսեւորում կանխարգելիչ բազմատեքստ ՝ ժամանակի կտրատման միջոցով:Այս սարսափելի տերմինի իմաստը բացատրվում է ստորև:

Բրինձ 10.2. Թելերի միջև անցում պարզ բազմալեզու ծրագրում

Թելերն ընդհատելիս և կառավարումն այլ թելերին փոխանցելիս օպերացիոն համակարգը կիրառում է ժամանակավոր կտրատման միջոցով կանխարգելիչ բազմաթելման սկզբունքը: Quantամանակի քվանտացումը լուծում է նաև բազմաթելային ծրագրերում նախկինում ծագած ընդհանուր խնդիրներից մեկը. Մեկ շարանը զբաղեցնում է պրոցեսորի ամբողջ ժամանակը և չի զիջում այլ թելերի վերահսկողությանը (որպես կանոն, դա տեղի է ունենում ինտենսիվ ցիկլերում, ինչպես վերը նշվածը): Պրոցեսորի բացառիկ հափշտակումը կանխելու համար ձեր թեմաները պետք է ժամանակ առ ժամանակ վերահսկողությունը փոխանցեն այլ թելերի: Եթե ​​ծրագիրը պարզվի, որ «անգիտակից» է, կա մեկ այլ, մի փոքր ավելի քիչ ցանկալի լուծում. Օպերացիոն համակարգը միշտ գերադասում է ընթացող շարանը ՝ անկախ դրա առաջնահերթ մակարդակից, այնպես որ պրոցեսորին հասանելիություն է տրվում համակարգի յուրաքանչյուր թելի:

Քանի որ: Մյուս կողմից, եթե .NET շրջանակը երբևէ հարմարեցվի այլ համակարգերի համար, դա կարող է փոխվել:

Եթե ​​նախքան Start- ը կանչելը մեր ծրագրում ներառենք հետևյալ տողը, ապա նույնիսկ ամենացածր առաջնահերթությամբ թելերը կստանան պրոցեսորի ժամանակի որոշ հատված.

bThread.Priority = ThreadPriority.Highest

Բրինձ 10.3. Ամենաբարձր առաջնահերթություն ունեցող շարանը սովորաբար ավելի արագ է սկսվում

Բրինձ 10.4. Պրոցեսորը նախատեսված է նաև ցածր առաջնահերթության թելերի համար

Հրամանը նոր շարանին տալիս է առավելագույն առաջնահերթություն և նվազեցնում հիմնական թելի առաջնահերթությունը: Նկ. 10.3 կարելի է տեսնել, որ նոր շարանը սկսում է ավելի արագ աշխատել, քան նախկինում, բայց, ինչպես Նկ. 10.4, հիմնական շարանը նույնպես վերահսկողություն է ստանումծուլություն (թեկուզև շատ կարճ ժամանակով և միայն հանելուց հոսքի երկարատև աշխատանքից հետո): Երբ ծրագիրը գործարկում եք ձեր համակարգիչներով, դուք կստանաք արդյունքներ, որոնք նման են Նկարում ներկայացվածներին: 10.3 և 10.4, բայց մեր համակարգերի միջև եղած տարբերությունների պատճառով ճշգրիտ համընկնում չի լինի:

ThreadPrlority թվարկված տեսակը ներառում է հինգ առաջնահերթ մակարդակի արժեքներ.

ThreadPriority. Ամենաբարձրը

ThreadPriority.AboveNormal

ThreadPrlority. Սովորական

ThreadPriority.BelowNormal

ThreadPriority. Ամենացածրը

Միացման մեթոդը

Երբեմն ծրագրի շարանը պետք է դադարեցվի, մինչև մեկ այլ շարանը ավարտվի: Ենթադրենք, դուք ցանկանում եք դադարեցնել շարանը 1, մինչև շարանը ավարտի իր հաշվարկը: Սրա համար հոսքից 1 Join մեթոդը կոչվում է հոսք 2. Այլ կերպ ասած `հրաման

թեմա 2. Միացեք ()

կասեցնում է ընթացիկ շարանը և սպասում 2 -րդ շարանի ավարտին: 1 -ին շարանը անցնում է դեպի փակ վիճակում:

Եթե ​​հոսքը 1 -ին միացնում եք հոսքին 2 -ին ՝ օգտագործելով Join մեթոդը, օպերացիոն համակարգը հոսքից 2 ինքնաբերաբար կսկսի հոսքը 1 -ը: Նշեք, որ գործարկման գործընթացը ոչ որոշիչ:անհնար է հստակ ասել, թե թելի 2 -ի ավարտից հետո որքա՞ն ժամանակ կսկսի գործել 1 -ը: Կա Join- ի մեկ այլ տարբերակ, որը վերադարձնում է բուլյան արժեքը.

թեմա 2. Միացեք (ամբողջ թիվ)

Այս մեթոդը կամ սպասում է թելի 2 -ի ավարտին, կամ արգելափակում է 1 -ի շարանը նշված ժամանակի ընդմիջումից հետո, ինչը պատճառ է դառնում, որ օպերացիոն համակարգի ժամանակացույցը նորից բաշխի պրոցեսորի ժամանակը շարանին: Մեթոդը վերադարձնում է True, եթե շարանը ավարտվում է մինչև նշված ընդմիջման ավարտը, իսկ հակառակ դեպքում ՝ False:

Հիշեք հիմնական կանոնը. 2 -րդ շարանը ավարտված է կամ ավարտված, դուք վերահսկողություն չունեք, երբ թելը 1 -ն ակտիվանա:

Թեմայի անուններ, CurrentThread և ThreadState

The Thread.CurrentThread հատկությունը վերադարձնում է տեղեկանք ներկայումս կատարվող թելի օբյեկտին:

Չնայած VB .NET- ում բազմաթել ծրագրերի վրիպազերծման հիանալի թելային պատուհան կա, որը նկարագրված է ստորև, բայց մեզ շատ հաճախ օգնեց հրամանը

MsgBox (Thread.CurrentThread.Name)

Հաճախ պարզվում էր, որ ծածկագիրը կատարվում է բոլորովին այլ թելից, որից ենթադրաբար պետք է կատարվեր:

Հիշեցնենք, որ «ծրագրերի հոսքերի ոչ-դետերմինիստական ​​պլանավորում» տերմինը նշանակում է շատ պարզ բան. Ծրագրավորողը գործնականում իր տրամադրության տակ չունի միջոցներ, որոնք կարող են ազդել ժամանակացույցի աշխատանքի վրա: Այդ պատճառով ծրագրերը հաճախ օգտագործում են ThreadState հատկությունը, որը տեղեկատվություն է վերադարձնում թելի ներկա վիճակի մասին:

Հոսքերի պատուհան

Visual Studio- ի Threads պատուհանը .NET- ը անգնահատելի է բազմաթել ծրագրերի վրիպազերծման գործում: Այն ակտիվացված է Debug> Windows ենթամենյուի հրամանով `ընդհատման ռեժիմում: Ենթադրենք, դուք bThread թեմային անուն եք նշանակել հետևյալ հրամանով.

bThread.Name = "Թել հանելը"

Ctrl + Break ստեղնաշարի համադրությամբ (կամ այլ կերպ) ծրագիրը ընդհատելուց հետո հոսքերի պատուհանի մոտավոր տեսքը ներկայացված է Նկ. 10.5.

Բրինձ 10.5. Հոսքերի պատուհան

Առաջին սյունակի սլաքը նշում է Thread.CurrentThread հատկությամբ վերադարձված ակտիվ շարանը: ID սյունակը պարունակում է թվային շարանի ID- ներ: Հաջորդ սյունակում նշվում են հոսքերի անունները (եթե նշանակված է): Տեղադրության սյունակը ցույց է տալիս գործարկման ընթացակարգը (օրինակ ՝ Console դասի WriteLine կարգը Նկար 10.5 -ում): Մնացած սյունակները պարունակում են տեղեկատվություն առաջնահերթ և կասեցված թելերի մասին (տե՛ս հաջորդ բաժինը):

Թելի պատուհանը (ոչ թե օպերացիոն համակարգը) Թույլ է տալիս վերահսկել ձեր ծրագրի թելերը `օգտագործելով համատեքստային ընտրացանկեր: Օրինակ, կարող եք կանգնեցնել ընթացիկ շարանը ՝ համապատասխան տողի վրա աջ սեղմելով և ընտրելով Freeze հրամանը (հետագայում, դադարեցված շարանը կարող է վերսկսվել): Թելերի դադարեցումը հաճախ օգտագործվում է կարգաբերման ժամանակ, որպեսզի թերի շարանը չխանգարի ծրագրին: Բացի այդ, հոսքերի պատուհանը թույլ է տալիս ակտիվացնել մեկ այլ (չկանգնված) հոսք. դա անելու համար աջ սեղմեք պահանջվող տողի վրա և համատեքստի ընտրացանկից ընտրեք Switch To Thread հրամանը (կամ պարզապես կրկնակի սեղմեք շարանի տողին): Ինչպես կցուցադրվի ստորև, սա շատ օգտակար է հավանական փակուղիների ախտորոշման համար:

Հոսքի դադարեցում

Еամանակավորապես չօգտագործված հոսքերը կարող են փոխանցվել պասիվ վիճակի ՝ օգտագործելով Slеer մեթոդը: Արգելափակված է համարվում նաև պասիվ հոսքը: Իհարկե, երբ շարանը դրվում է պասիվ վիճակում, մնացած թելերը կունենան ավելի շատ պրոցեսորային ռեսուրսներ: Slеer մեթոդի ստանդարտ շարահյուսությունը հետևյալն է. Thread.Sleep (interval_in_milliseconds)

Sleep կանչի արդյունքում ակտիվ շարանը դառնում է պասիվ առնվազն որոշակի քանակությամբ միլիվայրկյանների ընթացքում (այնուամենայնիվ, ակտիվացման ակտիվացումն նշված միջակայքի ավարտից անմիջապես հետո երաշխավորված չէ): Խնդրում ենք նկատի ունենալ. Մեթոդը կանչելիս հղում չի կատարվում որոշակի թեմայի. Sleep մեթոդը կոչվում է միայն ակտիվ շարանի համար:

Sleep- ի մեկ այլ տարբերակ ստիպում է ընթացիկ թեմային հրաժարվել CPU- ի հատկացված մնացած ժամանակից.

Թեման. Քուն (0)

Հաջորդ տարբերակը ընթացիկ շարանը դնում է պասիվ վիճակում անսահմանափակ ժամանակով (ակտիվացումը տեղի է ունենում միայն այն ժամանակ, երբ զանգում ես «Ընդհատել»).

Թեմա. Slеer (Timeout Infinite)

Քանի որ պասիվ թելերը (նույնիսկ անսահմանափակ ժամկետով) կարող են ընդհատվել ընդհատման մեթոդով, ինչը հանգեցնում է բացառությամբ ThreadlnterruptExcepti- ի մեկնարկի, Slayer զանգը միշտ փակված է Try-Catch բլոկում, ինչպես հետևյալ հատվածում.

Փորձիր

Թեման. Քուն (200)

«Թելի պասիվ վիճակը ընդհատվել է

Catch e Որպես բացառություն

«Այլ բացառություններ

Վերջ Փորձիր

Յուրաքանչյուր .NET ծրագիր աշխատում է ծրագրի թելի վրա, ուստի Sleep մեթոդը նույնպես օգտագործվում է ծրագրերը կասեցնելու համար (եթե Threadipg անվան տարածքը ծրագրի կողմից ներմուծված չէ, ապա պետք է օգտագործեք լիովին որակավորված անուն Threading.Thread. Sleep):

Programրագրի թեմաների ընդհատում կամ ընդհատում

Թեման ինքնաբերաբար կավարտվի, երբ մեթոդը նշվի, երբ ստեղծվում է ThreadStart պատվիրակը, բայց երբեմն անհրաժեշտ է լինում դադարեցնել մեթոդը (և, հետևաբար, շարանը), երբ առաջանում են որոշակի գործոններ: Նման դեպքերում հոսքերը սովորաբար ստուգում են պայմանական փոփոխական,նայած որի վիճակիորոշում է կայացվում հոսքից վթարային ելքի մասին: Սովորաբար, Do-while հանգույցը ներառված է դրա ընթացակարգում.

Sub ThreadedMethod ()

«Programրագիրը պետք է միջոցներ տրամադրի հարցման համար

«պայմանական փոփոխական.

«Օրինակ ՝ պայմանական փոփոխականը կարող է ձևավորվել որպես հատկություն

Do while conditionVariable = False And MoreWorkToDo

«Հիմնական ծածկագիրը

Loop End Sub

Պայմանական փոփոխականի հարցումը որոշ ժամանակ է պահանջում: Դուք պետք է օգտագործեք համառ հարցումներ օղակի վիճակում, եթե սպասում եք, որ շարանը վաղաժամ կավարտվի:

Եթե ​​պայմանի փոփոխականը պետք է ստուգվի որոշակի վայրում, ապա անսահմանափակ օղակում օգտագործեք If-Then հրամանը Exit Sub- ի հետ համատեղ:

Պայմանական փոփոխականի հասանելիությունը պետք է համաժամեցվի այնպես, որ այլ թելերից բացահայտումը չխանգարի դրա բնականոն օգտագործմանը: Այս կարևոր թեման ընդգրկված է «Խնդիրների վերացում. Համաժամացում» բաժնում:

Unfortunatelyավոք, պասիվ (կամ այլ կերպ արգելափակված) թելերի ծածկագիրը չի գործարկվում, ուստի պայմանական փոփոխականի հարցումներով տարբերակը նրանց համար հարմար չէ: Այս դեպքում զանգահարեք Interrupt մեթոդը օբյեկտի փոփոխականի վրա, որը պարունակում է հղում ցանկալի շարանին:

Ընդհատման մեթոդը կարող է կոչվել միայն Wait, Sleep կամ Join վիճակում գտնվող թեմաների վրա: Եթե ​​դուք կանչում եք «Ընդհատել» այն թելի համար, որը գտնվում է թվարկված վիճակներից մեկում, ապա որոշ ժամանակ անց շարանը նորից կսկսի աշխատել, և կատարման միջավայրը կսկսի ThreadlnterruptExcepti բացառությամբ շարանի: Դա տեղի է ունենում նույնիսկ այն դեպքում, երբ շարանը անվերջ պասիվացվել է ՝ Thread.Sleepdimeout զանգահարելով: Անսահման): Մենք ասում ենք «որոշ ժամանակ անց», քանի որ թելերի պլանավորումը ոչ որոշիչ է: Բացառությամբ ThreadlnterruptExcepti- ն բռնում է Catch բաժինը, որը պարունակում է սպասման վիճակից ելքի կոդը: Այնուամենայնիվ, Catch բաժինը պարտադիր չէ, որ շարանը ընդհատի ընդհատման կանչով. Շարանը, բացառության կարգով, վարում է այնպես, ինչպես իր կարծիքով հարմար է:

.NET- ում ընդհատման մեթոդը կարելի է անվանել նույնիսկ չարգելափակված թեմաների դեպքում: Այս դեպքում շարանը ընդհատվում է մոտակա արգելափակման ժամանակ:

Թելերի կասեցում և սպանում

Թելերի անվան տարածքը պարունակում է այլ մեթոդներ, որոնք ընդհատում են սովորական թելերը.

  • Կասեցնել;
  • Վիժեցնել

Դժվար է ասել, թե ինչու .NET- ը ներառում էր այս մեթոդների աջակցությունը. Մեթոդներից ոչ մեկը թույլ չի տալիս հոսքի բնականոն ապայնայնացում: Բացի այդ, «Կասեցնել» կամ «Ընդհատել» կոչելիս անհնար է կանխատեսել, թե թելը կասեցվելուց կամ ընդհատվելուց հետո ինչ վիճակում է թելը կթողնի առարկաները:

Աբորտին կանչելը նետում է ThreadAbortException: Որպեսզի հասկանաք, թե ինչու այս տարօրինակ բացառությունը չպետք է կիրառվի ծրագրերում, ահա .NET SDK փաստաթղթերից մի հատված.

«... Երբ շարանը ոչնչանում է Abort զանգահարելով, գործարկման ժամանակը նետում է ThreadAbortException: Սա բացառության հատուկ տեսակ է, որը հնարավոր չէ որսալ ծրագրի կողմից: Երբ այս բացառությունը նետվի, գործարկման ժամանակը գործարկում է վերջապես բոլոր բլոկները ՝ շարանը դադարեցնելուց առաջ: Քանի որ ցանկացած գործողություն կարող է տեղի ունենալ Վերջապես բլոկներում, զանգահարեք Join ՝ հոսքը քանդված լինելու համար »:

Բարոյականություն. Աբորտը և կասեցումը խորհուրդ չեն տրվում (և եթե դեռ չեք կարող անել առանց կասեցման, վերսկսեք կասեցված շարանը ՝ օգտագործելով Ռեզյումեի մեթոդը): Դուք կարող եք ապահով կերպով ավարտել շարանը միայն հարցման միջոցով համաժամանակացված վիճակի փոփոխականի միջոցով կամ զանգահարելով վերը քննարկված ընդհատման եղանակին:

Ֆոնային թելեր (դևեր)

Ֆոնի վրա աշխատող որոշ թելեր ինքնաբերաբար դադարում են աշխատել, երբ այլ ծրագրային բաղադրիչներ դադարում են: Մասնավորապես, աղբահավաքը աշխատում է ֆոնային թելերից մեկով: Սովորաբար ֆոնային թելերը ստեղծվում են տվյալներ ստանալու համար, բայց դա արվում է միայն այն դեպքում, երբ այլ թելեր աշխատում են ծածկագրով, որը կարող է մշակել ստացված տվյալները: Շարահյուսություն ՝ հոսքի անուն IsBackGround = Trueշմարիտ

Եթե ​​դիմումում մնացել են միայն ֆոնային թելեր, ծրագիրը ինքնաբերաբար կավարտվի:

Ավելի լուրջ օրինակ. HTML կոդից տվյալների հանում

Մենք խորհուրդ ենք տալիս հոսքեր օգտագործել միայն այն դեպքում, երբ ծրագրի ֆունկցիոնալությունը հստակորեն բաժանված է մի քանի գործողությունների: Լավ օրինակ է 9 -րդ գլխում HTML- ի արդյունահանման ծրագիրը: Մեր դասարանը երկու բան է անում. Amazon- ից տվյալների հավաքում և մշակում: Սա կատարյալ օրինակ է այն իրավիճակի, երբ բազմաթել ծրագրավորումը իսկապես տեղին է: Մենք ստեղծում ենք դասեր մի քանի տարբեր գրքերի համար, այնուհետև վերլուծում ենք տվյալները տարբեր հոսքերում: Յուրաքանչյուր գրքի համար նոր թեմա ստեղծելը մեծացնում է ծրագրի արդյունավետությունը, քանի որ մինչ մի թեմա ստանում է տվյալներ (ինչը կարող է պահանջել սպասել Amazon- ի սերվերում), մեկ այլ թեմա զբաղված կլինի արդեն ստացված տվյալների մշակմամբ:

Այս ծրագրի բազմաթել տարբերակն ավելի արդյունավետ է աշխատում, քան մեկ թելերով տարբերակը միայն մի քանի պրոցեսոր ունեցող համակարգչի վրա, կամ եթե լրացուցիչ տվյալների ընդունումը կարող է արդյունավետ կերպով զուգակցվել դրանց վերլուծության հետ:

Ինչպես նշվեց վերևում, միայն պարամետրեր չունեցող ընթացակարգերը կարող են գործարկվել թելերում, այնպես որ դուք ստիպված կլինեք փոքր փոփոխություններ կատարել ծրագրում: Ստորև բերված է հիմնական ընթացակարգը, որը վերաշարադրված է `պարամետրերը բացառելու համար.

Հանրային Sub FindRank ()

m_Rank = ScrapeAmazon ()

Console.WriteLine ("the rank of" & m_Name & "Is" & GetRank)

Վերջ ենթ

Քանի որ մենք չենք կարողանա օգտագործել համակցված դաշտը տեղեկատվության պահպանման և որոնման համար (գրաֆիկական ինտերֆեյսով բազմաթել ծրագրեր գրելը քննարկվում է այս գլխի վերջին բաժնում), ծրագիրը զանգվածում պահում է չորս գրքի տվյալները, որի սահմանումը սկսվում է այսպես.

Dim TheBook (3.1) As String theBook (0.0) = "1893115992"

theBook (0.l) = "mingրագրավորում VB .NET" "և այլն:

Չորս հոսքեր ստեղծվում են նույն ցիկլում, որոնցում ստեղծվում են AmazonRanker օբյեկտները.

I = 0 -ից 3 -ի համար

Փորձիր

theRanker = Նոր AmazonRanker (գիրք (i.0). theBookd.1))

aThreadStart = Նոր ThreadStar (հասցեն Of theRanker.FindRan ()

aThread = Նոր թեմա (aThreadStart)

aThread.Name = գիրքը (i.l)

aThread.Start () Catch e As Exception

Console.WriteLine (էլ. Հաղորդագրություն)

Վերջ Փորձիր

Հաջորդը

Ստորև ներկայացնում ենք ծրագրի ամբողջական տեքստը.

Ընտրանքների խիստ ներմուծման համակարգ. IO ներմուծում System.Net

Ներմուծման համակարգ. Թեմա

Մոդուլի մոդուլ

Ենթա հիմնական ()

Dim TheBook (3.1) Որպես լարային

գիրք (0.0) = "1893115992"

theBook (0.l) = "Bրագրավորում VB .NET"

գիրք (l.0) = "1893115291"

theBook (l.l) = "Տվյալների բազայի ծրագրավորում VB .NET"

գիրք (2,0) = "1893115623"

theBook (2.1) = "merրագրավորողի ներածություն C # - ին": "

գիրք (3.0) = "1893115593"

theBook (3.1) = "Gland the .Net հարթակ"

Dim i As Integer

Dim theRanker As = AmazonRanker

Dim aThreadStart As Threading.ThreadStart

Dim aThread As Threading.Thread

I = 0 -ից 3 -ի համար

Փորձիր

theRanker = Նոր AmazonRankerttheBook (i.0): գիրք (i.1))

aThreadStart = Նոր ThreadStart (հասցեից TheRanker. FindRank)

aThread = Նոր թեմա (aThreadStart)

aThread.Name = գիրքը (i.l)

aThread.Start ()

Catch e Որպես բացառություն

Console.WriteLlnete.Message)

Ավարտել Հաջորդը

Console.ReadLine ()

Վերջ ենթ

Ավարտի մոդուլ

Հանրային դաս AmazonRanker

Անձնական m_URL As String

Անձնական m_Rank As Integer

Անձնական m_Name As String

Public Sub New (ByVal ISBN As String. ByVal theName As String)

m_URL = "http://www.amazon.com/exec/obidos/ASIN/" & ISBN

m_Name = theName End Sub

Public Sub FindRank () m_Rank = ScrapeAmazon ()

Console.Writeline ("աստիճանը" & m_Name & "է"

& GetRank) Վերջ ենթ

Public Readonly Property GetRank () As String Get

Եթե ​​m_Rank<>0 Հետո

Վերադարձ CStr (m_Rank) Այլ

" Խնդիրներ

Վերջ, եթե

Վերջ Ստացեք

Վերջի գույքը

Հանրային միայն ընթերցվող սեփականության GetName () As String Get

Վերադարձնել m_Name- ը

Վերջ Ստացեք

Վերջի գույքը

Անձնական գործառույթ ScrapeAmazon () As Integer Try

Նվազեցնել theURL- ը որպես նոր ուրու (m_URL)

Dim theRequest- ը որպես WebRequest

theRequest = WebRequest.Create (theURL)

Dim theResponse- ը որպես WebResponse

theResponse = theRequest.GetResponse

Dim aReader As New StreamReader (theResponse.GetResponseStream ())

Թուլացրեք տվյալները որպես լարային

theData = aReader.ReadToEnd

Վերադարձի վերլուծություն (theData)

Catch E Որպես բացառություն

Console.WriteLine (E.Message)

Console.WriteLine (E.StackTrace)

Վահանակ ReadLine ()

Վերջ Փորձեք վերջ գործառույթը

Անձնական գործառույթի վերլուծություն (ByVal theData As String) որպես ամբողջ թիվ

Dim Location As.Integer Location = theData.IndexOf (" Amazon.com

Վաճառքի վարկանիշ.") _

+ "Amazon.com վաճառքի վարկանիշ.".Երկարություն

Dim temp As լարային

Կատարել մինչև theData.Substring (Location.l) = "<" temp = temp

& theData.Substring (Որտեղից. l) Որտեղից + = 1 օղակ

Վերադարձ Clnt (ջերմաստիճան)

Ավարտի գործառույթը

Ավարտի դաս

Բազմաշերտ գործողությունները սովորաբար օգտագործվում են .NET և I / O անունների տարածքներում, ուստի .NET Framework գրադարանը նրանց տրամադրում է հատուկ ասինխրոն մեթոդներ: Բազմաթելային ծրագրեր գրելիս ասինխրոն մեթոդների օգտագործման վերաբերյալ լրացուցիչ տեղեկությունների համար տե՛ս HTTPWebRequest դասի BeginGetResponse և EndGetResponse մեթոդները:

Հիմնական վտանգ (ընդհանուր տվյալներ)

Մինչ այժմ դիտարկվել է թելերի միակ անվտանգ օգտագործման դեպքը ` մեր հոսքերը չեն փոխել ընդհանուր տվյալները:Եթե ​​թույլ տաք ընդհանուր տվյալների փոփոխությունը, հավանական սխալները սկսում են արագորեն բազմապատկվել, և ծրագրի համար դրանցից ազատվելը դառնում է շատ ավելի դժվար: Մյուս կողմից, եթե արգելում եք համօգտագործվող տվյալների փոփոխումը տարբեր թելերով, ապա բազմաշերտ .NET ծրագրավորումը դժվար թե տարբերվի VB6- ի սահմանափակ հնարավորություններից:

Attentionանկանում ենք ձեր ուշադրությունը հրավիրել մի փոքրիկ ծրագրի վրա, որը ցույց է տալիս ծագած խնդիրները ՝ առանց ավելորդ մանրամասների մեջ մտնելու: Այս ծրագիրը նմանակում է յուրաքանչյուր սենյակում գտնվող թերմոստատ ունեցող տունը: Եթե ​​ջերմաստիճանը 5 աստիճան فارենհայտ կամ ավելի (մոտ 2.77 աստիճան ցելսիուս) ցածր է նպատակային ջերմաստիճանից, մենք հրամայում ենք ջեռուցման համակարգին բարձրացնել ջերմաստիճանը 5 աստիճանով; հակառակ դեպքում ջերմաստիճանը բարձրանում է ընդամենը 1 աստիճանով: Եթե ​​ընթացիկ ջերմաստիճանը սահմանվածից մեծ է կամ հավասար, ապա փոփոխություն չի կատարվում: Յուրաքանչյուր սենյակում ջերմաստիճանի վերահսկումն իրականացվում է առանձին հոսքով `200 միլիվայրկյան ուշացումով: Հիմնական աշխատանքը կատարվում է հետևյալ հատվածով.

Եթե ​​mHouse.HouseTemp< mHouse.MAX_TEMP = 5 Then Try

Թեման. Քուն (200)

Catch tie As ThreadlnterruptException

«Պասիվ սպասումն ընդհատվել է

Catch e Որպես բացառություն

«Փորձեք այլ բացառություններ

mHouse.HouseTemp + - 5 "և այլն

Ստորև բերված է ծրագրի ամբողջական կոդը: Արդյունքը ցուցադրվում է Նկ. 10.6. Տանը ջերմաստիճանը հասել է 105 աստիճանի Ֆարենհայտ (40.5 աստիճան Celsius):

1 տարբերակ Խիստ միացված

2 ներմուծման համակարգ. Թեմա

3 Մոդուլի մոդուլ

4 ենթաօրենսդրական ()

5 Dim myHouse As New House (l0)

6 Վահանակ: ReadLine ()

7 Վերջ ենթ

8 Ավարտի մոդուլ

9 Հանրային դասի տուն

10 Հասարակական կոնստրուկտոր MAX_TEMP որպես ամբողջ թիվ = 75

11 Անձնական mCurTemp As Integer = 55

12 Անձնական mRooms () որպես սենյակ

13 Public Sub New (ByVal numOfRooms As Integer)

14 ReDim mRooms (numOfRooms = 1)

15 Dim i As Integer

16 Dim aThreadStart As Threading.ThreadStart

17 Dim aThread As Thread

18 For i = 0 To numOfRooms -1

19 Փորձիր

20 mRooms (i) = NewRoom (Me, mCurTemp, CStr (i) և «throom»)

21 aThreadStart - Նոր ThreadStart (Հասցե `_

mRooms (i) .CheckTempInRoom)

22 aThread = Նոր թեմա (aThreadStart)

23 aThread.Start ()

24 Catch E Որպես բացառություն

25 Console.WriteLine (E.StackTrace)

26 Վերջ փորձիր

27 Հաջորդը

28 Վերջ ենթ

29 Հանրային սեփականություն HouseTemp () որպես ամբողջ թիվ

երեսուն: Ստացեք

31 Վերադարձ mCurTemp

32 Վերջ ստանալը

33 հավաքածու (ByVal արժեքը որպես ամբողջ թիվ)

34 mCurTemp = Արժեք 35 վերջի հավաքածու

36 Վերջի գույք

37 Վերջ դաս

38 հանրային դասասենյակ

39 Անձնական mCurTemp As Integer

40 Անձնական mName As String

41 Անձնական mHouse As House

42 Public Sub New (ByVal theHouse As House,

ByVal temp As Integer, ByVal roomName As String)

43 mHouse = theHouse

44 mCurTemp = ջերմաստիճան

45 մ Անուն = սենյակի Անուն

46 Վերջ ենթ

47 Public Sub CheckTempInRoom ()

48 Փոխել ջերմաստիճանը ()

49 Վերջ ենթ

50 մասնավոր ստորաբաժանում ChangeTemperature ()

51 Փորձիր

52 Եթե mHouse.HouseTemp< mHouse.MAX_TEMP - 5 Then

53 Թեման: Քուն (200)

54 mHouse. HouseTemp + - 5

55 Console.WriteLine ("Am in" & Me.mName & _

56 ".Ներկա ջերմաստիճանը" & mHouse.HouseTemp)

57. Ինքն իրեն mHouse.HouseTemp< mHouse.MAX_TEMP Then

58 Թեման: Քուն (200)

59 mHouse.HouseTemp + = 1

60 Console.WriteLine ("Am in" & Me.mName & _

61 ".Ներկա ջերմաստիճանը" & mHouse.HouseTemp)

62 Այլ

63 Console.WriteLine ("Am in" & Me.mName & _

64 ".Ներկա ջերմաստիճանը" & mHouse.HouseTemp)

65 «Ոչինչ մի արեք, ջերմաստիճանը նորմալ է

66 Վերջ Եթե

67 Catch tae As ThreadlnterruptionException

68 «Պասիվ սպասումն ընդհատվել է

69 Catch e As Exception

70 «Այլ բացառություններ

71 Վերջ փորձիր

72 Վերջ ենթ

73 Վերջ դաս

Բրինձ 10.6. Բազմալեզու խնդիրներ

Sub Main ընթացակարգը (տողեր 4-7) ստեղծում է «տուն» ՝ տասը «սենյակով»: House- ի դասը սահմանում է առավելագույնը 75 աստիճան Fahrenheit (մոտ 24 աստիճան Celsius): 13-28 տողերը սահմանում են բավականին բարդ տան շինարար: Understandingրագիրը հասկանալու բանալին 18-27 տողերն են: Տող 20 -ը ստեղծում է սենյակի մեկ այլ օբյեկտ, և տան օբյեկտի մասին հղումը փոխանցվում է կոնստրուկտորին, որպեսզի անհրաժեշտության դեպքում սենյակի առարկան կարողանա անդրադառնալ դրան: 21-23 տողերում սկսվում է տասը հոսք `յուրաքանչյուր սենյակում ջերմաստիճանը կարգավորելու համար: Սենյակի դասը սահմանվում է 38-73 տողերում: House coxpa տեղեկանքպահվում է mHouse փոփոխականում Room դասի կոնստրուկտորում (տող 43): Theերմաստիճանի ստուգման և ճշգրտման ծածկագիրը (տողեր 50-66) կարծես պարզ և բնական է, բայց ինչպես շուտով կտեսնեք, այս տպավորությունը խաբում է: Նկատի ունեցեք, որ այս ծածկագիրը փաթաթված է Try-Catch բլոկի մեջ, քանի որ ծրագիրը օգտագործում է Sleep մեթոդը:

Դժվար թե որևէ մեկը համաձայնի ապրել 105 աստիճան فارենհայտ (40,5 -ից 24 աստիճան Celsius) ջերմաստիճանի պայմաններում: Ինչ է պատահել? Խնդիրը կապված է հետևյալ տողի հետ.

Եթե ​​mHouse.HouseTemp< mHouse.MAX_TEMP - 5 Then

Եվ տեղի է ունենում հետևյալը. Նախ ՝ ջերմաստիճանը ստուգվում է հոսքով 1. Նա տեսնում է, որ ջերմաստիճանը չափազանց ցածր է, և այն բարձրացնում է 5 աստիճանով: Unfortunatelyավոք, նախքան ջերմաստիճանի բարձրացումը, հոսքը 1 -ն ընդհատվում է, և կառավարումը փոխանցվում է հոսքին 2. Հոսք 2 -ը ստուգում է նույն փոփոխականը, որը դեռ չի փոխվելհոսք 1. Այսպիսով, հոսքը 2 -ը նույնպես պատրաստվում է բարձրացնել ջերմաստիճանը 5 աստիճանով, բայց ժամանակ չունի դա անելու համար և նույնպես անցնում է սպասողական վիճակի: Գործընթացը շարունակվում է մինչև հոսքի 1 -ի ակտիվացումը և անցնում է հաջորդ հրամանին `ջերմաստիճանը բարձրացնելով 5 աստիճանով: Աճը կրկնվում է, երբ բոլոր 10 հոսքերը միացված են, և տան բնակիչները վատ ժամանակ կունենան:

Խնդրի լուծում. Համաժամացում

Նախորդ ծրագրում իրավիճակ է ստեղծվում, երբ ծրագրի ելքը կախված է թելերի կատարման կարգից: Դրանից ազատվելու համար դուք պետք է համոզվեք, որ հրամանները հավանում են

Եթե ​​mHouse.HouseTemp< mHouse.MAX_TEMP - 5 Then...

ամբողջությամբ մշակվում են ակտիվ թելի կողմից ՝ մինչև այն ընդհատվելը: Այս հատկությունը կոչվում է ատոմային ամոթ -կոդի բլոկը պետք է կատարվի յուրաքանչյուր շարանի կողմից առանց ընդհատումների, որպես ատոմային միավոր: Հրամանների խումբը, որը համակցված է ատոմային բլոկի մեջ, չի կարող ընդհատվել թելերի ժամանակացույցի կողմից մինչև դրա ավարտը: Multանկացած բազմաթել ծրագրավորման լեզու ունի ատոմայնություն ապահովելու իր ուղիները: VB .NET- ում SyncLock հրահանգն օգտագործելու ամենահեշտ ձևը զանգահարելիս օբյեկտի փոփոխականով անցնելն է: Փոքր փոփոխություններ կատարեք ChangeTemperature ընթացակարգում նախորդ օրինակից, և ծրագիրը լավ կաշխատի.

Անձնական ենթակառուցվածք ChangeTemperature () SyncLock (mHouse)

Փորձիր

Եթե ​​mHouse.HouseTemp< mHouse.MAXJTEMP -5 Then

Թեման. Քուն (200)

mHouse.HouseTemp + = 5

Console.WriteLine ("Am in" & Me.mName & _

". Ընթացիկ ջերմաստիճանը" & mHouse.HouseTemp)

Ինքն իրեն

mHouse.HouseTemp< mHouse. MAX_TEMP Then

Թեման. Քուն (200) mHouse.HouseTemp + = 1

Console.WriteLine ("Am in" & Me.mName & _ ". Ընթացիկ ջերմաստիճանը" & mHouse.HomeTemp) Այլ

Console.WriteLineC "Am in" & Me.mName & _ ": Ընթացիկ ջերմաստիճանը" & mHouse.HouseTemp)

«Ոչինչ մի արեք, ջերմաստիճանը նորմալ է

End If Catch tie As ThreadlnterruptionException

«Պասիվ սպասումը ընդհատեց Catch e As Exception- ը

«Այլ բացառություններ

Վերջ Փորձիր

Ավարտել SyncLock- ը

Վերջ ենթ

SyncLock բլոկի ծածկագիրը կատարվում է ատոմային եղանակով: Մնացած բոլոր թելերից դրա մուտքը փակ կլինի, մինչև առաջին շարանը չթողնի կողպումը End SyncLock հրամանով: Եթե ​​համաժամանակացված բլոկում շարանը անցնում է պասիվ սպասման վիճակի, կողպեքը մնում է մինչև շարանը ընդհատվելը կամ վերսկսվելը:

SyncLock հրամանի ճիշտ օգտագործումը ապահով է պահում ձեր ծրագրի շարանը: Unfortunatelyավոք, SyncLock- ի չարաշահումը բացասաբար է անդրադառնում կատարման վրա: Բազմաթելային ծրագրում ծածկագրի համաժամացումը մի քանի անգամ նվազեցնում է դրա աշխատանքի արագությունը: Համաժամացրեք միայն ամենաանհրաժեշտ ծածկագիրը և հնարավորինս արագ բացեք կողպեքը:

Հավաքածուի բազային դասերն անապահով են բազմաթել ծրագրերում, սակայն .NET Framework- ը ներառում է հավաքածուի դասերի մեծ մասի թելերով ապահով տարբերակներ: Այս դասարաններում պոտենցիալ վտանգավոր մեթոդների ծածկագիրը ներառված է SyncLock բլոկներում: Հավաքածուի դասերի թելերով անվտանգ տարբերակները պետք է օգտագործվեն բազմաթել ծրագրերում, որտեղ տվյալների ամբողջականությունը վտանգված է:

Մնում է նշել, որ պայմանական փոփոխականները հեշտությամբ իրականացվում են SyncLock հրահանգի միջոցով: Դա անելու համար պարզապես անհրաժեշտ է գրությունը համաժամեցնել ընդհանուր բուլյան հատկության հետ, որը մատչելի է ընթերցման և գրելու համար, ինչպես դա արվում է հետևյալ հատվածում.

Հանրային դասի վիճակ Փոփոխական

Անձնական ընդհանուր պահարան ՝ որպես օբյեկտ = Նոր օբյեկտ ()

Անձնական համօգտագործված mOK As Boolean Shared

Property TheConditionVariable () Որպես բուլյան

Ստացեք

Վերադառնալ mOK

Վերջ Ստացեք

Սահմանել (ByVal Value As Boolean) SyncLock (պահարան)

mOK = Արժեք

Ավարտել SyncLock- ը

Վերջի հավաքածու

Վերջի գույքը

Ավարտի դաս

SyncLock հրամանի և մոնիտորի դաս

SyncLock հրամանի օգտագործումը ներառում է որոշ նրբություններ, որոնք ցուցադրված չեն վերը նշված պարզ օրինակներում: Այսպիսով, համաժամացման օբյեկտի ընտրությունը շատ կարևոր դեր է խաղում: Փորձեք նախորդ ծրագիրը գործարկել SyncLock (Me) հրամանով SyncLock- ի (mHouse) փոխարեն: Theերմաստիճանը կրկին բարձրանում է շեմից:

Հիշեք, որ SyncLock հրամանը համաժամացվում է ՝ օգտագործելով օբյեկտ,անցել է որպես պարամետր, այլ ոչ թե կոդի հատվածով: SyncLock պարամետրը գործում է որպես այլ թելերից համաժամացված հատված մուտք գործելու դուռ: SyncLock (Me) հրամանը իրականում բացում է մի քանի տարբեր «դռներ», ինչը հենց այն է, ինչ դուք փորձում էիք խուսափել համաժամացման դեպքում: Բարոյականություն.

Բազմաբնույթ հավելվածում համօգտագործվող տվյալները պաշտպանելու համար SyncLock հրահանգը պետք է միաժամանակ համաժամացնի մեկ օբյեկտ:

Քանի որ համաժամացումը կապված է որոշակի օբյեկտի հետ, որոշ իրավիճակներում հնարավոր է ակամա կողպել այլ բեկորներ: Ենթադրենք, դուք ունեք երկու համաժամեցված մեթոդ ՝ առաջին և երկրորդ, և երկու մեթոդներն էլ համաժամեցված են bigLock օբյեկտի վրա: Երբ շարանը 1 առաջինը մուտքագրում է մեթոդը և գրավում bigLock- ը, ոչ մի թեմա չի կարող երկրորդը մուտքագրել մեթոդը, որովհետև դրան հասանելիությունը արդեն սահմանափակված է թել 1 -ով:

SyncLock հրամանի ֆունկցիոնալությունը կարելի է համարել որպես Monitor դասի ֆունկցիոնալության ենթաբազմություն: Մոնիտորների դասը շատ հարմարեցված է և կարող է օգտագործվել համաժամացման ոչ աննշան առաջադրանքներ լուծելու համար: SyncLock հրամանը Moni tor դասի Enter և Exi t մեթոդների մոտավոր անալոգն է.

Փորձիր

Մոնիտոր: Մուտքագրեք (առարկան) Վերջապես

Մոնիտոր. Ելք (theObject)

Վերջ Փորձիր

Որոշ ստանդարտ գործողությունների համար (փոփոխականի ավելացում / նվազում, երկու փոփոխականի բովանդակության փոխանակում) .NET Framework- ը տրամադրում է Փակված դասը, որի մեթոդները կատարում են այդ գործողությունները ատոմային մակարդակում: Օգտագործելով Interlocked դասը ՝ այս գործողությունները շատ ավելի արագ են, քան SyncLock հրամանի օգտագործումը:

Փոխկապակցված

Համաժամացման ընթացքում կողպեքը տեղադրվում է ոչ թե թելերի, այլ օբյեկտների վրա, այնպես որ օգտագործելիս տարբերվողարգելափակման առարկաներ տարբերվող programsրագրերում կոդի հատվածներ երբեմն տեղի են ունենում բավականին աննշան սխալներ: Unfortunatelyավոք, շատ դեպքերում մեկ օբյեկտի վրա համաժամացումը պարզապես անընդունելի է, քանի որ դա կհանգեցնի թելերի չափազանց հաճախակի արգելափակման:

Հաշվի առեք իրավիճակը փոխկապակցված(փակուղի) իր ամենապարզ տեսքով: Պատկերացրեք երկու ծրագրավորողների ճաշի սեղանի շուրջ: Unfortunatelyավոք, նրանք ունեն միայն մեկ դանակ և մեկ պատառաքաղ երկուսի համար: Ենթադրելով, որ ուտելու համար ձեզ հարկավոր է և՛ դանակ, և՛ պատառաքաղ, հնարավոր է երկու իրավիճակ.

  • Programրագրավորողներից մեկին հաջողվում է դանակ և պատառաքաղ բռնել և սկսում ուտել: Երբ կուշտ է, նա ճաշը մի կողմ է դնում, իսկ հետո մեկ այլ ծրագրավորող կարող է դրանք տանել:
  • Մեկ ծրագրավորողը վերցնում է դանակը, իսկ մյուսը ՝ պատառաքաղը: Ոչ մեկը չի կարող սկսել ուտել, եթե մյուսը չթողնի իր սարքը:

Բազմաթղթային ծրագրում այս իրավիճակը կոչվում է փոխադարձ արգելափակում:Երկու մեթոդները համաժամեցված են տարբեր օբյեկտների վրա: Thread A- ն գրավում է 1 -ին օբյեկտը և մտնում է այս օբյեկտով պաշտպանված ծրագրի մասը: Unfortunatelyավոք, աշխատելու համար նրան անհրաժեշտ է մուտք գործել մեկ այլ Sync Lock- ով պաշտպանված ծածկագիր ՝ համաժամացման այլ օբյեկտով: Բայց մինչ ժամանակ կհասցնի մտնել մի հատված, որը սինխրոնիզացված է մեկ այլ օբյեկտի կողմից, B հոսքը մտնում է այն և գրավում այս օբյեկտը: Այժմ A շարանը չի կարող մտնել երկրորդ հատվածը, B շարանը չի կարող մտնել առաջին հատվածի մեջ, և երկու թելերը դատապարտված են անվերջ սպասելու: Ոչ մի թեմա չի կարող շարունակել աշխատել, քանի որ պահանջվող օբյեկտը երբեք չի ազատվի:

Փակուղիների ախտորոշումը բարդանում է նրանով, որ դրանք կարող են առաջանալ համեմատաբար հազվադեպ դեպքերում: Ամեն ինչ կախված է այն կարգից, որով ժամանակացույցը CPU- ի ժամանակ է հատկացնում նրանց: Հնարավոր է, որ շատ դեպքերում համաժամացման օբյեկտները կգրավվեն ոչ փակուղային կարգով:

Ստորև բերված է հենց նկարագրված փակուղային իրավիճակի իրականացում: Ամենակարևոր կետերի կարճ քննարկումից հետո մենք ցույց կտանք, թե ինչպես կարելի է թելերի պատուհանում բացահայտել փակուղային իրավիճակը.

1 տարբերակ Խիստ միացված

2 ներմուծման համակարգ. Թեմա

3 Մոդուլի մոդուլ

4 ենթաօրենսդրական ()

5 Dim Tom որպես նոր ծրագրավորող («Թոմ»)

6 Dim Bob որպես նոր ծրագրավորող («Bob»)

7 Dim aThreadStart As New ThreadStart (Հասցե Tom.Eat)

8 Dim aThread որպես նոր թեմա (aThreadStart)

9 aThread.Name = "Թոմ"

10 Dim bThreadStart As New ThreadStarttAddressOf Bob.Eat)

11 Dim bThread As New Thread (bThreadStart)

12 bThread.Name = "Բոբ"

13 aThread.Start ()

14 bThread.Start ()

15 Վերջ ենթ

16 Ավարտի մոդուլ

17 Հանրային դասի պատառաքաղ

18 Անձնական համօգտագործված mForkAvaiTable As Boolean = True

19 Անձնական ընդհանուր սեփականատեր As String = "Ոչ ոք"

20 Անձնական միայն կարդալու սեփականություն OwnsUtensil () As String

21 Ստացեք

22 Վերադառնալ սեփականատեր

23 Վերջ Ստացեք

24 Վերջի գույք

25 Public Sub GrabForktByVal a As Programmer)

26 Console.Writel_ine (Thread.CurrentThread.Name & _

«փորձում է բռնել պատառաքաղը»)

27 Console.WriteLine (Me.OwnsUtensil & «ունի պատառաքաղ»): ...

28 Մոնիտոր: Մուտքագրեք (ես) "SyncLock (aFork)"

29 Եթե mFork Հասանելի է Ապա

30 ա. HasFork = Trueշմարիտ

31 սեփականատեր = a.MyName

32 mFork Հասանելի = Կեղծ

33 Console.WriteLine (a.MyName & «հենց պատառաքաղն է սպասել»)

34 Փորձիր

Թեման. Քուն (100) Catch e As Exception Console.WriteLine (e.StackTrace)

Վերջ Փորձիր

35 Վերջ Եթե

36 Մոնիտոր: Ելք (Ես)

Ավարտել SyncLock- ը

37 Վերջ ենթ

38 Ավարտի դաս

39 Հանրային դասի դանակ

40 Անձնական համօգտագործվող mKnife Հասանելի է որպես բուլյան = ճշմարիտ

41 Անձնական ընդհանուր սեփականատեր As String = "Ոչ ոք"

42 Անձնական միայն կարդալու սեփականություն OwnsUtensi1 () As String

43 Ստացեք

44 Վերադառնալ սեփականատեր

45 Վերջ Ստացեք

46 Վերջավորություն

47 Public Sub GrabKnifetByVal a As Programmer)

48 Console.WriteLine (Thread.CurrentThread.Name & _

«փորձում է դանակը բռնել»)

49 Console.WriteLine (Me.OwnsUtensil & «ունի դանակը»)

50 Մոնիտոր: Մուտքագրեք (ես) "SyncLock (aKnife)"

51 Եթե mKnife Հասանելի է Ապա

52 mKnifeAvailable = Կեղծ

53 ա. HasKnife = շմարիտ

54 mOwner = a.MyName

55 Console.WriteLine (a.MyName & «հենց նոր ստացա դանակը սպասելով»)

56 Փորձեք

Թեման. Քուն (100)

Catch e Որպես բացառություն

Console.WriteLine (e.StackTrace)

Վերջ Փորձիր

57 Վերջ Եթե

58 Մոնիտոր: Ելք (Ես)

59 Վերջ ենթ

60 Վերջ դաս

61 Հանրային դասի ծրագրավորող

62 Անձնական mName As String

63 Անձնական համօգտագործվող mFork As Fork

64 Անձնական համօգտագործվող mKnife As Knife

65 Անձնական mHasKnife As Boolean

66 Անձնական mHasFork As Boolean

67 Shared Sub New ()

68 mFork = Նոր պատառաքաղ ()

69 mKnife = Նոր դանակ ()

70 Վերջ ենթ

71 Public Sub New (ByVal theName As String)

72 մ Անուն = Անուն

73 Վերջ ենթ

74 Հանրային միայն ընթերցվող սեփականություն MyName () As String

75 Ստացեք

76 Վերադառնալ mName

77 Վերջ Ստացեք

78 Վերջի գույք

79 Հասարակական սեփականություն HasKnife () As Boolean

80 Ստացեք

81 Վերադարձ mHasKnife

82 Վերջ ստանալ

83 հավաքածու (ByVal Value As Boolean)

84 mHasKnife = Արժեք

85 Վերջ հավաքածու

86 Վերջի սեփականություն

87 Հասարակական սեփականություն HasFork () As Boolean

88 Ստացեք

89 Վերադարձ mHasFork

90 Վերջ Ստացեք

91 հավաքածու (ByVal Value As Boolean)

92 mHasFork = Արժեք

93 Վերջ հավաքածու

94 Վերջի գույք

95 հանրային սնունդ ()

96 Do Heta Me.HasKnife And Me.HasFork

97 Console.Writeline (Thread.CurrentThread.Name & "թեմայում է")

98 Եթե Rnd ()< 0.5 Then

99 mFork.GrabFork (Ես)

100 ուրիշ

101 mKnife.GrabKnife (Ես)

102 Վերջ Եթե

103 Օղակ

104 MsgBox (Me.MyName & «կարող եմ ուտել»)

105 mKnife = Նոր դանակ ()

106 mFork = Նոր պատառաքաղ ()

107 Վերջ ենթ

108 Վերջ դաս

Հիմնական ընթացակարգը Main (տողեր 4-16) ստեղծում է merրագրավորողի դասի երկու օրինակ և այնուհետև սկսում է երկու թեմա ՝ merրագրավորող դասի կրիտիկական Eat մեթոդը գործարկելու համար (տողեր 95-108), որոնք նկարագրված են ստորև: Հիմնական ընթացակարգը սահմանում է թելերի անունները և կարգավորում դրանք. հավանաբար ամեն ինչ, որ տեղի է ունենում, հասկանալի է և առանց մեկնաբանության:

Fork դասի ծածկագիրն ավելի հետաքրքիր տեսք ունի (տողեր 17-38) (Դանակի նմանատիպ դասը սահմանվում է 39-60 տողերում): 18 -րդ և 19 -րդ տողերում նշվում են ընդհանուր դաշտերի արժեքները, որոնցով կարող եք պարզել, թե արդյոք վարդակն այժմ հասանելի է, և եթե ոչ, ով է այն օգտագործում: ReadOnly սեփականությունը OwnUtensi1 (տողեր 20-24) նախատեսված է տեղեկատվության ամենապարզ փոխանցման համար: Fork դասի կենտրոնական մասը GrabFork «grab the fork» մեթոդն է ՝ սահմանված 25-27 տողերում:

  1. 26 -րդ և 27 -րդ տողերը պարզապես տպում են վրիպազերծման մասին տեղեկությունները մխիթարելիս: Մեթոդի հիմնական ծածկագրում (տողեր 28-36), պատառաքաղի մուտքը համաժամացվում է ըստ օբյեկտիգոտի Me. Քանի որ մեր ծրագիրը օգտագործում է միայն մեկ պատառաքաղ, Me sync- ն ապահովում է, որ երկու թել չկարողանա միաժամանակ բռնել այն: Slee "p հրահանգը (34 -րդ տողից սկսվող բլոկում) նմանակում է պատառաքաղ / դանակ բռնելու և ուտելու սկիզբը: Նկատի ունեցեք, որ Sleep հրամանը չի բացում օբյեկտները և միայն արագացնում է փակուղիները:
    Այնուամենայնիվ, ամենահետաքրքիրը Programmer դասի ծածկագիրն է (տողեր 61-108): 67-70 տողերում սահմանվում է ընդհանուր կոնստրուկտոր `ապահովելու համար, որ ծրագրում կա միայն մեկ պատառաքաղ և դանակ: Սեփականության ծածկագիրը (տողեր 74-94) պարզ է և մեկնաբանություն չի պահանջում: Ամենակարևորը տեղի է ունենում Eat մեթոդով, որը կատարվում է երկու առանձին թելերով: Գործընթացը շարունակվում է հանգույցով, մինչև որ ինչ -որ հոսք դանակի հետ միասին գրավում է պատառաքաղը: 98-102 տողերում օբյեկտը պատահականորեն բռնում է պատառաքաղը / դանակը ՝ օգտագործելով Rnd կանչը, ինչն էլ առաջացնում է փակուղի: Տեղի է ունենում հետևյալը.
    Thoth- ը, որը կատարում է Thoth օբյեկտի Eat մեթոդը, կանչվում է և մտնում հանգույց: Նա բռնում է դանակը և անցնում սպասողական վիճակի:
  2. Bob's Eat մեթոդը կատարող շարանը կանչվում է և մտնում հանգույց: Այն չի կարող բռնել դանակը, բայց բռնում է պատառաքաղը և անցնում սպասողական վիճակի:
  3. Thoth- ը, որը կատարում է Thoth օբյեկտի Eat մեթոդը, կանչվում է և մտնում հանգույց: Նա փորձում է բռնել պատառաքաղը, բայց Բոբն արդեն բռնել է պատառաքաղը; շարանը անցնում է սպասողական վիճակի:
  4. Bob's Eat մեթոդը կատարող շարանը կանչվում է և մտնում հանգույց: Նա փորձում է բռնել դանակը, բայց դանակն արդեն գրավված է օբյեկտի Թոթով; շարանը անցնում է սպասողական վիճակի:

Այս ամենը շարունակվում է անորոշ ժամանակով. Մենք կանգնած ենք տիպիկ փակուղու հետ (փորձեք գործարկել ծրագիրը և կտեսնեք, որ ոչ ոք ի վիճակի չէ այս կերպ սնվել):
Կարող եք նաև ստուգել, ​​թե արդյոք փակուղի է առաջացել թելերի պատուհանում: Գործարկեք ծրագիրը և ընդհատեք այն Ctrl + Break ստեղներով: Ներառեք Me փոփոխականը տեսադաշտում և բացեք հոսքերի պատուհանը: Արդյունքը նման է Նկարում պատկերվածին: 10.7. Նկարում պատկերված է, որ Բոբի թելը բռնել է դանակը, բայց այն պատառաքաղ չունի: Աջ սեղմեք Tot տողի Threads պատուհանում և համատեքստի ընտրացանկից ընտրեք Switch to Thread հրամանը: Տեսադաշտը ցույց է տալիս, որ Թոթ հոսքն ունի պատառաքաղ, բայց դանակ չունի: Իհարկե, սա հարյուր տոկոսանոց ապացույց չէ, բայց նման պահվածքը գոնե կասկածի տեղիք է տալիս, որ ինչ -որ բան այն չէ:
Եթե ​​մեկ օբյեկտի համաժամացման տարբերակը (ինչպես տանը `ջերմաստիճանի բարձրացումով) հնարավոր չէ, փոխադարձ կողպեքները կանխելու համար կարող եք համարակալել համաժամացման օբյեկտները և դրանք մշտապես գրավել մշտական ​​կարգով: Եկեք շարունակենք ճաշելու ծրագրավորողի անալոգիան. Եթե շարանը միշտ առաջինը վերցնում է դանակը, այնուհետև պատառաքաղը, փակուղու հետ կապված խնդիրներ չեն առաջանա: Դանակը բռնող առաջին հոսքը կկարողանա նորմալ սնվել: Transրագրային հոսքերի լեզվով թարգմանված ՝ սա նշանակում է, որ 2 -րդ օբյեկտի գրավումը հնարավոր է միայն այն դեպքում, եթե առաջին օբյեկտն առաջինը գրավված է:

Բրինձ 10.7. Թելերի պատուհանում փակուղիների վերլուծություն

Հետևաբար, եթե մենք հեռացնենք 98 -րդ տողում Rnd զանգը և այն փոխարինենք հատվածով

mFork.GrabFork (Ես)

mKnife.GrabKnife (Ես)

փակուղին անհետանում է:

Համագործակցեք տվյալների ստեղծման ժամանակ

Բազմաթելային ծրագրերում հաճախ հանդիպում է մի իրավիճակ, երբ թելերը ոչ միայն աշխատում են ընդհանուր տվյալների հետ, այլև սպասում են դրանց հայտնվելուն (այսինքն ՝ շարանը 1 -ը պետք է ստեղծի տվյալներ, նախքան թելը 2 -ն օգտագործի): Քանի որ տվյալները համօգտագործվում են, դրանց հասանելիությունը պետք է համաժամեցվի: Անհրաժեշտ է նաև միջոցներ տրամադրել պատրաստի տվյալների տեսքի վերաբերյալ սպասող թեմաներին ծանուցելու համար:

Այս իրավիճակը սովորաբար կոչվում է մատակարարի / սպառողի խնդիրը:Թելը փորձում է մուտք գործել դեռ գոյություն չունեցող տվյալներ, ուստի այն պետք է վերահսկողությունը փոխանցի մեկ այլ թելի, որը ստեղծում է անհրաժեշտ տվյալները: Խնդիրը լուծվում է հետևյալ ծածկագրով.

  • Թեմա 1 -ը (սպառող) արթնանում է, մուտքագրում է համաժամեցված մեթոդ, փնտրում է տվյալներ, չի գտնում դրանք և անցնում սպասման վիճակի: Նախապեսֆիզիկապես, նա պետք է հեռացնի արգելափակումը, որպեսզի չխանգարի մատակարարող թելի աշխատանքին:
  • 2 -րդ շարանը (մատակարարը) մուտքագրում է 1 -ին շարքով ազատված համաժամեցված մեթոդ, ստեղծում էտվյալները հոսքի 1 -ի համար և ինչ -որ կերպ տեղեկացնում է հոսքին 1 -ին տվյալների առկայության մասին: Այնուհետև այն ազատում է կողպեքը, որպեսզի շարանը 1 կարողանա մշակել նոր տվյալները:

Մի փորձեք լուծել այս խնդիրը ՝ անընդհատ կանչելով 1 -ին շարանը և ստուգելով պայմանի փոփոխականի վիճակը, որի արժեքը> սահմանված է թել 2 -ով: Այս որոշումը լրջորեն կազդի ձեր ծրագրի աշխատանքի վրա, քանի որ շատ դեպքերում 1 -ի շարանը կանչվել առանց պատճառի. և շարանը 2 այնքան հաճախ կսպասի, որ տվյալներ ստեղծելու համար ժամանակ չի սպառվի:

Պրովայդեր / սպառող հարաբերությունները շատ տարածված են, ուստի ծրագրավորման բազմալեզու գրադարաններում նման իրավիճակների համար ստեղծվում են հատուկ պարզունակներ: NET- ում այս պարզունակները կոչվում են Wait և Pulse-PulseAl 1 և մտնում են Monitor դասի մեջ: Նկար 10.8 -ը ցույց է տալիս այն իրավիճակը, որը մենք պատրաստվում ենք ծրագրել: Organizրագիրը կազմակերպում է թելի երեք հերթ `սպասման հերթ, արգելափակման հերթ և կատարման հերթ: Թեմաների ժամանակացույցը չի հատկացնում պրոցեսորի ժամանակը սպասման հերթում գտնվող թեմաներին: Թելի համար ժամանակ հատկացնելու համար այն պետք է անցնի կատարման հերթ: Արդյունքում, դիմումի աշխատանքը կազմակերպվում է շատ ավելի արդյունավետ, քան պայմանական փոփոխականի սովորական հարցումը:

Կեղծ ծածկագրում տվյալների սպառողական արտահայտությունը ձևակերպվում է հետևյալ կերպ.

«Մուտք գործեք հետևյալ տիպի համաժամեցված բլոկի մեջ

Մինչդեռ տվյալներ չկան

Գնացեք սպասման հերթ

Օղակ

Եթե ​​կան տվյալներ, մշակեք դրանք:

Թողնել համաժամեցված բլոկը

Wait հրամանը կատարելուց անմիջապես հետո շարանը կասեցվում է, կողպեքն ազատվում է, և շարանը մտնում է սպասման հերթ: Երբ կողպեքն ազատվում է, կատարման հերթում շարանը թույլատրվում է գործարկել: Timeամանակի ընթացքում մեկ կամ ավելի արգելափակված թելեր կստեղծեն սպասման հերթում գտնվող թելի գործարկման համար անհրաժեշտ տվյալներ: Քանի որ տվյալների վավերացումը կատարվում է օղակի մեջ, տվյալների օգտագործման անցումը (հանգույցից հետո) տեղի է ունենում միայն այն ժամանակ, երբ առկա են պատրաստի մշակման տվյալներ:

Կեղծ կոդում տվյալների մատակարարի արտահայտությունը հետևյալն է.

«Մտնելով համաժամացված դիտման բլոկ

Մինչդեռ տվյալները ՉԻ անհրաժեշտ

Գնացեք սպասման հերթ

Այլապես արտադրել տվյալներ

Երբ տվյալները պատրաստ են, զանգահարեք Pulse-PulseAll:

արգելափակման հերթից մեկ կամ մի քանի թել տեղափոխելու կատարման հերթ: Թողեք համաժամեցված բլոկը (և վերադառնաք գործարկման հերթ)

Ենթադրենք, մեր ծրագիրը նմանակում է ընտանիքը մեկ ծնողով, ով փող է աշխատում և երեխան, ով ծախսում է այդ գումարը: Երբ գումարն ավարտվիպարզվում է, որ երեխան պետք է սպասի նոր գումարի ժամանմանը: Այս մոդելի ծրագրային ապահովումն ունի հետևյալ տեսքը.

1 տարբերակ Խիստ միացված

2 ներմուծման համակարգ. Թեմա

3 Մոդուլի մոդուլ

4 ենթաօրենսդրական ()

5 Dim theFamily As New Family ()

6 theFamily.StartltsLife ()

7 Վերջ ենթ

8 Վերջ fjodule

9

10 Հանրային դասի ընտանիք

11 Անձնական mMoney As Integer

12 Անձնական mWeek As Integer = 1

13 Public Sub StartltsLife ()

14 Dim aThreadStart As New ThreadStarUAddressOf.Produce)

15 Dim bThreadStart As New ThreadStarUAddressOf. Սպառեք)

16 Dim aThread As New Thread (aThreadStart)

17 Dim bThread As New Thread (bThreadStart)

18 aThread.Name = "Արտադրել"

19 aThread.Start ()

20 bThread.Name = "Սպառում"

21 բ Թեման. Սկսել ()

22 Վերջ ենթ

23 Հանրային սեփականության շաբաթ () որպես ամբողջական թիվ

24 Ստացեք

25 Վերադարձ շաբաթական շաբաթ

26 Վերջ Ստացեք

27 հավաքածու (ByVal արժեքը որպես ամբողջ թիվ)

28 շաբաթ - Արժեք

29 Վերջ հավաքածու

30 Վերջի գույք

31 Հասարակական սեփականություն OurMoney () որպես ամբողջ թիվ

32 Ստացեք

33 Վերադարձեք դրամական միջոցներ

34 Վերջ ստացեք

35 հավաքածու (ByVal արժեքը որպես ամբողջ թիվ)

36 մլն դրամ = Արժեք

37 Վերջ հավաքածու

38 Վերջի գույք

39 հանրային ենթաարտադրություն ()

40 թեմա: Քուն (500)

41 անել

42 Մոնիտոր: Մուտքագրեք (ես)

43 Do while Me.OurMoney> 0

44 Մոնիտոր: Սպասեք (ես)

45 օղակ

46 Me.OurMoney = 1000

47 Մոնիտոր: PulseAll (Ես)

48 Մոնիտոր: Ելք (Ես)

49 Օղակ

50 Վերջ ենթ

51 հանրային ենթաօգտագործում ()

52 MsgBox («Ես սպառման թելում եմ»)

53 անել

54 Մոնիտոր: Մուտքագրեք (ես)

55 Do while Me.OurMoney = 0

56 Մոնիտոր: Սպասեք (ես)

57 Օղակ

58 Console.WriteLine («Հարգելի ծնող, ես պարզապես ծախսել եմ ձեր ամբողջը» & _

գումար շաբաթվա ընթացքում »և TheWeek)

59 Շաբաթ + = 1

60 Եթե շաբաթը = 21 * 52 Այնուհետև համակարգ. Շրջակա միջավայր: Ելք (0)

61 Me.OurMoney = 0

62 Monitor.PulseAll (Ես)

63 Մոնիտոր: Ելք (ես)

64 Օղակ

65 Վերջ ենթ

66 Վերջ դաս

StartltsLife մեթոդը (տողեր 13-22) պատրաստվում է սկսել Արտադրել և սպառել հոսքերը: Ամենակարևորը տեղի է ունենում Արտադրել (տողեր 39-50) և Սպառում (տողեր 51-65) հոսքերում: Sub Produce ընթացակարգը ստուգում է գումարի առկայությունը, և եթե գումար կա, այն անցնում է սպասման հերթին: Հակառակ դեպքում, ծնողը գումար է ստեղծում (տող 46) և սպասում հերթի օբյեկտներին տեղեկացնում իրավիճակի փոփոխության մասին: Նկատի ունեցեք, որ Pulse-Pulse All- ի կանչն ուժի մեջ է մտնում միայն այն դեպքում, երբ կողպեքը բաց է թողնվում Monitor.Exit հրամանով: Եվ ընդհակառակը, Sub Consume ընթացակարգը ստուգում է գումարի առկայությունը, և եթե փող չկա, այդ մասին տեղեկացնում է ապագա ծնողին: 60 տողը պարզապես դադարեցնում է ծրագիրը 21 պայմանական տարուց հետո. զանգերի համակարգ. Շրջակա միջավայր: Exit (0) - ը End հրահանգի .NET անալոգն է (End հրամանը նույնպես աջակցվում է, բայց ի տարբերություն System. Environment- ի: Ելք, այն չի վերադարձնում օպերացիոն համակարգին ելքի կոդը):

Սպասման հերթում դրված թեմաները պետք է ազատվեն ձեր ծրագրի այլ մասերից: Այս պատճառով է, որ մենք նախընտրում ենք օգտագործել PulseAll- ը Pulse- ի փոխարեն: Քանի որ նախապես հայտնի չէ, թե որ շարանը կակտիվանա, երբ Pulse 1 -ը կանչվի, հերթում համեմատաբար փոքր թվով թելեր կան, կարող եք նույնքան լավ զանգահարել PulseAll:

Բազմաշերտ գրաֆիկական ծրագրերում

GUI- ի ծրագրերում բազմալեզվության մեր քննարկումը սկսվում է մի օրինակով, որը բացատրում է, թե ինչի համար է GUI հավելվածներում բազմաթելը: Ստեղծեք ձև երկու կոճակներով ՝ Start (btnStart) և Cancel (btnCancel), ինչպես ցույց է տրված նկ. 10.9. «Սկսել» կոճակին սեղմելը առաջացնում է դաս, որը պարունակում է պատահական տող ՝ 10 միլիոն նիշ և մեթոդ ՝ այդ երկար տողում «E» տառի առաջացման դեպքերը հաշվելու համար: Նկատի ունեցեք StringBuilder դասի օգտագործումը երկար տողերի ավելի արդյունավետ ստեղծման համար:

Քայլ 1

Թեման 1 -ը նկատում է, որ դրա համար տվյալներ չկան: Այն զանգում է «Սպասիր», ազատում է կողպեքը և անցնում սպասման հերթ:



Քայլ 2

Երբ կողպեքն ազատվում է, շարանը 2 կամ շարանը թողնում է բլոկի հերթը և մտնում համաժամեցված բլոկ ՝ ձեռք բերելով կողպեքը

Քայլ 3

Եկեք ասենք, որ շարանը 3 մտնում է համաժամեցված բլոկ, ստեղծում է տվյալներ և կանչում Pulse-Pulse All:

Բլոկից դուրս գալուց և կողպեքը բաց թողնելուց անմիջապես հետո 1 շարանը տեղափոխվում է կատարման հերթ: Եթե ​​շարանը 3 զանգում է Pluse, ապա միայն մեկը մտնում է կատարման հերթthread, երբ Pluse All- ը կանչվում է, բոլոր թեմաները գնում են կատարման հերթ:



Բրինձ 10.8. Մատակարարի / սպառողի խնդիր

Բրինձ 10.9. Multithreading պարզ GUI հավելվածում

Ներմուծման համակարգ. Տեքստ

Հանրային դասի Պատահական կերպարներ

Անձնական m_Data As StringBuilder

Անձնական mjength, m_count As Integer

Public Sub New (ByVal n As Integer)

m_ Երկարություն = n -1

m_Data = Նոր StringBuilder (m_length) MakeString ()

Վերջ ենթ

Անձնական ենթակետ MakeString ()

Dim i As Integer

Dim myRnd As New Random ()

I = 0 - ից m_length

«Ստեղծեք պատահական թիվ 65 -ից 90 -ի միջև,

«փոխարկել այն մեծատառի

"և կցեք StringBuilder օբյեկտին

m_Data.Append (Chr (myRnd.Next (65.90)))

Հաջորդը

Վերջ ենթ

Public Sub StartCount ()

GetEes ()

Վերջ ենթ

Անձնական ենթակետ GetEes ()

Dim i As Integer

I = 0 - ից m_length

Եթե ​​m_Data.Chars (i) = CChar ("E") Ապա

m_հաշիվ + = 1

Վերջ, եթե հաջորդը

m_CountDone = Trueիշտ է

Վերջ ենթ

Հասարակական ոչ միայն ընթերցման համար

Գույքի GetCount () Ինչպես ամբողջ թիվ ստանալ

Եթե ​​ոչ (m_CountDone) Ապա

Վերադարձնել m_count

Վերջ, եթե

Վերջ Ստացեք վերջնական հատկություն

Հասարակական ոչ միայն ընթերցման համար

Գույքը IsDone () Ինչպես բուլյան ստացեք

Վերադառնալ

m_CountDone

Վերջ Ստացեք

Վերջի գույքը

Ավարտի դաս

Ձևի երկու կոճակների հետ կապված շատ պարզ կոդ կա: Btn-Start_Click ընթացակարգը հաստատում է վերը նշված RandomCharacters դասը, որը ներառում է 10 միլիոն նիշ ունեցող տող.

Անձնական ենթակետ btnStart_Click (ByVal ուղարկողը որպես System.Object.

ByVal e As System.EventArgs) Բռնակներ btnSTart. Սեղմեք

Dim RC As New RandomCharacters (10000000)

RC.StartCount ()

MsgBox («The es of number is» & RC.GetCount)

Վերջ ենթ

Չեղարկել կոճակը ցուցադրում է հաղորդագրության տուփ.

Անձնական ենթակետ btnCancel_Click (ByVal ուղարկողը որպես System.Object._

ByVal e As System.EventArgs) Բռնակներ btnCancel.Click

MsgBox («Հաշիվն ընդհատվեց»)

Վերջ ենթ

Երբ ծրագիրը գործարկվում է, և սեղմվում է «Սկսել» կոճակը, պարզվում է, որ «Չեղարկել» կոճակը չի արձագանքում օգտվողի մուտքին, քանի որ շարունակական հանգույցը խանգարում է կոճակին կարգավորել իր ստացած իրադարձությունը: Սա անընդունելի է ժամանակակից ծրագրերում:

Երկու հնարավոր լուծում կա: Առաջին տարբերակը, որը հայտնի է VB- ի նախորդ տարբերակներից, հրաժարվում է բազմաթելից. DoEvents զանգը ներառված է օղակում: NET- ում այս հրամանն ունի հետևյալ տեսքը.

Դիմում. DoEvents ()

Մեր օրինակում դա հաստատ ցանկալի չէ. Ո՞վ է ցանկանում դանդաղեցնել տասը միլիոն DoEvents զանգերով ծրագիրը: Եթե ​​փոխարենը հանգույցը հատկացնեք առանձին թելի, ապա օպերացիոն համակարգը կանցնի թելերի միջև, և Չեղարկել կոճակը կմնա ֆունկցիոնալ: Ստորև ներկայացված է առանձին թելերով իրականացումը: Հստակ ցույց տալու համար, որ Չեղարկել կոճակը գործում է, երբ մենք սեղմում ենք այն, մենք պարզապես դադարեցնում ենք ծրագիրը:

Հաջորդ քայլը ՝ Countուցադրել հաշվարկի կոճակը

Ենթադրենք, դուք որոշեցիք ցուցադրել ձեր ստեղծագործական երևակայությունը և ձևին հաղորդել նկ. 10.9. Խնդրում ենք նկատի ունենալ. Show Count կոճակը դեռ հասանելի չէ:

Բրինձ 10.10. Կողպված կոճակի ձև

Ակնկալվում է, որ առանձին թեմա կանի հաշվարկը և կողպում է անհասանելի կոճակը: Սա, իհարկե, կարելի է անել. ընդ որում, նման առաջադրանք առաջանում է բավականին հաճախ: Unfortunatelyավոք, դուք չեք կարողանա գործել ամենաակնառու ձևով ՝ երկրորդական շարանը կապեք GUI շարանի հետ ՝ կոնստրուկտորում պահելով հղումը ShowCount կոճակին կամ նույնիսկ օգտագործելով ստանդարտ պատվիրակ: Այլ կերպ ասած, երբեքմի օգտագործեք ստորև նշված տարբերակը (հիմնական սխալտողերը հաստ են)

Հանրային դասի Պատահական կերպարներ

Անձնական m_0ata As StringBuilder

Անձնական m_CountDone As Boolean

Անձնական mjength. m_count As Integer

Անձնական m_Button As Windows.Forms.Button

Public Sub New (ByVa1 n As Integer, _

ByVal b As Windows.Forms.Button)

մ_երկար = n - 1

m_Data = Նոր StringBuilder (mJength)

m_Button = b MakeString ()

Վերջ ենթ

Անձնական ենթակետ MakeString ()

Dim I As Integer

Dim myRnd As New Random ()

I = 0 -ից մինչև m_length

m_Data.Append (Chr (myRnd.Next (65.90)))

Հաջորդը

Վերջ ենթ

Public Sub StartCount ()

GetEes ()

Վերջ ենթ

Անձնական ենթակետ GetEes ()

Dim I As Integer

I = 0 -ից մինչև mjength

Եթե ​​m_Data.Chars (I) = CChar ("E") Հետո

m_հաշիվ + = 1

Վերջ, եթե հաջորդը

m_CountDone = Trueիշտ է

m_Button.Enabled = True

Վերջ ենթ

Հասարակական ոչ միայն ընթերցման համար

Գույքի GetCount () որպես ամբողջ թիվ

Ստացեք

Եթե ​​ոչ (m_CountDone) Ապա

Նետեք նոր բացառություն («Հաշվարկը դեռ ավարտված չէ») Այլապես

Վերադարձնել m_count

Վերջ, եթե

Վերջ Ստացեք

Վերջի գույքը

Հասարակական միայն ընթերցվող սեփականություն IsDone () որպես բուլյան

Ստացեք

Վերադարձնել m_CountDone

Վերջ Ստացեք

Վերջի գույքը

Ավարտի դաս

Հավանական է, որ այս կոդը որոշ դեպքերում կգործի: Այնուամենայնիվ.

  • Երկրորդային թելի փոխազդեցությունը GUI ստեղծող թելի հետ չի կարող կազմակերպվել ակնհայտ էնշանակում է.
  • Երբեքմի փոփոխեք գրաֆիկական ծրագրերի տարրերը այլ ծրագրերի հոսքերից: Բոլոր փոփոխությունները պետք է տեղի ունենան միայն GUI ստեղծող շարանում:

Եթե ​​դուք խախտում եք այս կանոնները, մենք երաշխավորում ենքայդ նուրբ, նուրբ սխալները կառաջանան ձեր բազմաթել գրաֆիկական ծրագրերում:

Նաև չի հաջողվի կազմակերպել իրադարձությունների օգտագործմամբ օբյեկտների փոխազդեցությունը: 06 միջոցառման աշխատողը վազում է նույն թեմայով, որը կոչվում էր RaiseEvent, որպեսզի իրադարձությունները ձեզ չօգնեն:

Այնուամենայնիվ, ողջամտությունը թելադրում է, որ գրաֆիկական ծրագրերը պետք է ունենան այլ թելից տարրեր փոփոխելու միջոց: NET Framework- ում կա թելից անվտանգ միջոց ՝ GUI հավելվածների մեթոդները մեկ այլ թեմայից կանչելու համար: Մեթոդի Invoker պատվիրակ համակարգից: Այդ նպատակով օգտագործվում է Windows անվանումների տարածքը: Ձևաթղթեր: Ստորև բերված հատվածը ցույց է տալիս GetEes մեթոդի նոր տարբերակը (փոփոխված տողերը համարձակ):

Անձնական ենթակետ GetEes ()

Dim I As Integer

I = 0 -ից մինչև m_length

Եթե ​​m_Data.Chars (I) = CChar ("E") Հետո

m_հաշիվ + = 1

Վերջ, եթե հաջորդը

m_CountDone = Trueշմարիտ փորձ

Dim mylnvoker As New Methodlnvoker (հասցեի UpDateButton)

myInvoker.Invoke () Catch e As ThreadlnterruptException

«Անհաջողություն

Վերջ Փորձիր

Վերջ ենթ

Հանրային ենթակետ UpDateButton ()

m_Button.Enabled = Trueիշտ է

Վերջ ենթ

Կոճակի միջտելային զանգերը կատարվում են ոչ թե ուղղակիորեն, այլ Method Invoker- ի միջոցով: .NET Framework- ը երաշխավորում է, որ այս տարբերակը թելի համար անվտանգ է:

Ինչու՞ են այդքան շատ խնդիրներ բազմաթել ծրագրավորման հետ կապված:

Այժմ, երբ դուք որոշակի պատկերացում ունեք բազմալեզու և դրա հետ կապված հնարավոր խնդիրների մասին, մենք որոշեցինք, որ նպատակահարմար կլինի հարցին պատասխանել այս ենթաբաժնի վերնագրում ՝ այս գլխի վերջում:

Պատճառներից մեկն այն է, որ բազմաթերթը ոչ գծային գործընթաց է, և մենք սովոր ենք գծային ծրագրավորման մոդելին: Սկզբում դժվար է ընտելանալ այն մտքին, որ ծրագրի կատարումը կարող է պատահականորեն ընդհատվել, և վերահսկողությունը կփոխանցվի այլ կոդի:

Այնուամենայնիվ, կա ևս մեկ, առավել հիմնարար պատճառ. Մեր օրերում ծրագրավորողները չափազանց հազվադեպ են ծրագրավորում հավաքողում, կամ գոնե նայում են կազմողի ապամոնտաժված ելքին: Հակառակ դեպքում, նրանց համար շատ ավելի հեշտ կլիներ ընտելանալ այն մտքին, որ հավաքման տասնյակ հրահանգներ կարող են համապատասխանել բարձր մակարդակի լեզվի (օրինակ ՝ VB .NET) մեկ հրամանին: Թելը կարող է ընդհատվել այս հրահանգներից որևէ մեկից հետո, և, հետևաբար, բարձր մակարդակի հրամանի մեջտեղում:

Բայց սա դեռ ամենը չէ. Ժամանակակից կոմպիլյատորները օպտիմալացնում են ծրագրի կատարումը, և համակարգչային սարքավորումները կարող են միջամտել հիշողության կառավարմանը: Արդյունքում, կոմպիլյատորը կամ սարքավորումը կարող են փոխել ծրագրի աղբյուրի կոդում նշված հրամանների կարգը ՝ առանց ձեր իմացության [ Շատ կոմպիլյատորներ օպտիմալացնում են զանգվածների ցիկլային պատճենումը, ինչպիսիք են i = 0 -ից n: b (i) = a (i): ncxt: Կոմպիլյատորը (կամ նույնիսկ մասնագիտացված հիշողության կառավարիչը) կարող է պարզապես ստեղծել զանգված, այնուհետև այն լրացնել մեկ պատճենահանման գործողությամբ ՝ առանձին տարրեր բազմիցս պատճենելու փոխարեն:].

Հուսանք, այս բացատրությունները կօգնեն ձեզ ավելի լավ հասկանալ, թե ինչու է բազմաթել ծրագրավորումը առաջացնում այդքան շատ խնդիրներ, կամ գոնե ավելի քիչ զարմանում ձեր բազմաթել ծրագրերի տարօրինակ վարքագծի վրա:

Պարզ բազմաշերտ դիմում կառուցելու օրինակ:

Bնվել է Դելֆիում բազմալեզու ծրագրեր կառուցելու վերաբերյալ բազմաթիվ հարցերի պատճառով:

Այս օրինակի նպատակն է ցույց տալ, թե ինչպես ճիշտ կառուցել բազմաթելային ծրագիր ՝ երկար թելերով հեռացնելով առանձին թել: Եվ ինչպես, նման դիմումում, ապահովել հիմնական շարանի փոխազդեցությունը աշխատողի հետ ՝ ձևից (տեսողական բաղադրիչներից) հոսք տվյալներ փոխանցելու և հակառակը:

Օրինակը չի պնդում, որ ամբողջական է, այն միայն ցուցադրում է թելերի փոխազդեցության ամենապարզ եղանակները: Թույլ տալով օգտվողին «արագ կուրացնել» (ով կիմանար, թե որքան եմ ատում դա) ճիշտ աշխատող բազմաթելային ծրագիր:
Ամեն ինչ դրանում մանրամասնորեն մեկնաբանվում է (իմ կարծիքով), բայց եթե հարցեր ունեք, տվեք:
Բայց ևս մեկ անգամ զգուշացնում եմ ձեզ. Հոսքերը հեշտ չեն... Եթե ​​պատկերացում չունեք, թե ինչպես է ամեն ինչ աշխատում, ապա հսկայական վտանգ կա, որ հաճախ ամեն ինչ ձեզ մոտ լավ կաշխատի, և երբեմն ծրագիրը իրեն ավելի քան տարօրինակ կպահի: Սխալ գրված բազմալեզու ծրագրի վարքագիծը մեծապես կախված է մեծ թվով գործոններից, որոնք երբեմն չեն կարող վերարտադրվել կարգաբերման ժամանակ:

Այսպիսով, օրինակ. Հարմարության համար ես տեղադրել եմ և ծածկագիրը, և արխիվը կցել եմ մոդուլին և ձևի կոդին

միավոր ExThreadForm;

օգտագործում է
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Երկխոսություններ, StdCtrls;

// հաստատուններ, որոնք օգտագործվում են հոսքից տվյալներ փոխանցելիս ՝ օգտագործելով
// ուղարկել պատուհանի հաղորդագրություններ
const
WM_USER_SendMessageMetod = WM_USER + 10;
WM_USER_PostMessageMetod = WM_USER + 11;

տիպ
// թելի դասի նկարագրություն, tThread- ի ժառանգ
tMyThread = դաս (tThread)
մասնավոր
SyncDataN: Ամբողջական;
SyncDataS: Լարային;
ընթացակարգ SyncMetod1;
պաշտպանված
ընթացակարգ Կատարել; անտեսել;
հասարակական
Պարամետր 1 ՝ լարային;
Պարամետր 2 ՝ ամբողջական;
Param3: Բուլյան;
Կանգնեցրեց ՝ բուլյան;
LastRandom: Ամբողջական;
IterationNo: Ամբողջ;
ResultList: tStringList;

Կոնստրուկտորի ստեղծում (aParam1: String);
ավերող Ոչնչացնել; անտեսել;
վերջ;

// հոսքի օգտագործմամբ ձևի դասի նկարագրություն
TForm1 = դաս (TForm)
Պիտակ 1: TLabel;
Հիշեցում 1: TMemo;
btnStart: T կոճակ;
btnStop: T կոճակ;
Խմբագրել 1 ՝ TEdit;
Խմբագրել 2 ՝ TEdit;
CheckBox1: TCheckBox;
Պիտակ 2: TLabel;
Պիտակ 3: TLabel;
Պիտակ 4 ՝ TLabel;
ընթացակարգ btnStartClick (Ուղարկող ՝ TObject);
ընթացակարգ btnStopClick (Ուղարկող ՝ TObject);
մասնավոր
(Անձնական հայտարարագրեր)
MyThread: tMyThread;
ընթացակարգ EventMyThreadOnTerminate (Ուղարկող ՝ tObject);
ընթացակարգ EventOnSendMessageMetod (var Msg: TMessage); հաղորդագրություն WM_USER_SendMessageMetod;
ընթացակարգ EventOnPostMessageMetod (var Msg: TMessage); հաղորդագրություն WM_USER_PostMessageMetod;

Հանրային
(Հրապարակային հայտարարություններ)
վերջ;

var
Ձև 1: TForm1;

{
Stopped - ցույց է տալիս տվյալների փոխանցումը ձևից հոսք:
Լրացուցիչ համաժամացումը չի պահանջվում, քանի որ այն պարզ է
միայնակ տիպ, և գրված է միայն մեկ շարանով:
}

ընթացակարգ TForm1.btnStartClick (Ուղարկող ՝ TObject);
սկսել
Պատահականացնել (); // հաջորդականության մեջ պատահականության ապահովում Random () - ով ոչ մի կապ չունի հոսքի հետ

// Ստեղծեք հոսքի օբյեկտի օրինակ ՝ այն փոխանցելով մուտքային պարամետր
{
Ո ATՇԱԴՐՈԹՅՈՆ:
Հոսքի կոնստրուկտորը գրված է այնպես, որ հոսքը ստեղծվի
կասեցվել է, քանի որ այն թույլ է տալիս.
1. Վերահսկեք դրա գործարկման պահը: Սա գրեթե միշտ ավելի հարմար է, քանի որ
թույլ է տալիս ստեղծել հոսք նույնիսկ սկսելուց առաջ, փոխանցել դրա մուտքը
պարամետրեր և այլն
2. Որովհետեւ Ստեղծված օբյեկտի հղումը կպահվի ձևի դաշտում, այնուհետև
թելի ինքնաոչնչացումից հետո (տես ստորև), որը երբ շարանը վազում է
կարող է առաջանալ ցանկացած պահի, այս հղումն անվավեր կդառնա:
}
MyThread: = tMyThread.Create (Form1.Edit1.Text);

// Այնուամենայնիվ, քանի որ շարանը ստեղծվել է կասեցված, ապա ցանկացած սխալի վրա
// դրա սկզբնավորման ընթացքում (սկսելուց առաջ), մենք ինքներս պետք է այն քանդենք
// այն, ինչ մենք օգտագործում ենք try / εκτός block
փորձիր

// Թելի ավարտի կարգավորիչի նշանակում, որում մենք կստանանք
// հոսքի աշխատանքի արդյունքները և «վերաշարադրել» դրա հղումը
MyThread.OnTerminate: = EventMyThreadOnTerminate;

// Քանի որ արդյունքները կհավաքվեն OnTerminate- ում, այսինքն. ինքնաոչնչացումից առաջ
// հոսքը, ապա մենք կհանենք այն ոչնչացնելու մտահոգությունները
MyThread.FreeOnTerminate: = Trueշմարիտ;

// Մուտքային պարամետրերի հոսքի օբյեկտի դաշտերի միջով անցման օրինակ
// ակնթարթային, երբ դեռ չի աշխատում:
// Անձամբ ես նախընտրում եմ դա անել անտեսված պարամետրերի միջոցով
// կոնստրուկտոր (tMyThread.Create)
MyThread.Param2: = StrToInt (Form1.Edit2.Text);

MyThread.Stopped: = Կեղծ; // մի տեսակ պարամետր, նույնպես, բայց փոխվում է
// թեմայի գործարկման ժամանակը
բացառությամբ
// քանի որ շարանը դեռ չի սկսվել և չի կարողանա ինքնաոչնչանալ, մենք այն «ձեռքով» կկործանենք
FreeAndNil (MyThread);
// և ապա թող բացառությունը գործի ինչպես միշտ
բարձրացնել;
վերջ;

// Քանի որ թեմայի օբյեկտը հաջողությամբ ստեղծվել և կազմաձևվել է, ժամանակն է սկսել այն
MyThread.Resume;

ShowMessage («Հոսքը սկսվեց»);
վերջ;

ընթացակարգ TForm1.btnStopClick (Ուղարկող ՝ TObject);
սկսել
// Եթե շարանի օրինակը դեռ գոյություն ունի, ապա խնդրեք այն դադարեցնել
// Եվ, հենց «հարցնել»: Սկզբունքորեն մենք կարող ենք նաեւ «պարտադրել», բայց դա կստացվի
// ծայրահեղ անհետաձգելի տարբերակ, որը պահանջում է այս ամենի հստակ պատկերացում
// հոսող խոհանոց: Հետեւաբար, այստեղ դա չի դիտարկվում:
եթե նշանակված է (MyThread), ապա
MyThread.Stopped: = Trueիշտ է
ուրիշ
ShowMessage («Թելը չի ​​գործարկվում»);
վերջ;

ընթացակարգ TForm1.EventOnSendMessageMetod (var Msg: TMessage);
սկսել
// համաժամանակյա հաղորդագրության մշակման եղանակ
// WParam- ում tMyThread օբյեկտի հասցեն, LParam- ում ՝ շարանի LastRandom- ի ընթացիկ արժեքը
tMyThread- ով (Msg.WParam) իսկապես սկսվում է
Form1.Label3.Caption: = Ձևաչափ ("% d% d% d",);
վերջ;
վերջ;

ընթացակարգ TForm1.EventOnPostMessageMetod (var Msg: TMessage);
սկսել
// ասինխրոն հաղորդագրության մշակման եղանակ
// WParam- ում IterationNo- ի ընթացիկ արժեքը, LParam- ում LastRandom հոսքի ընթացիկ արժեքը
Form1.Label4.Caption: = Ձևաչափ ("% d% d",);
վերջ;

ընթացակարգ TForm1.EventMyThreadOnTerminate (Ուղարկող ՝ tObject);
սկսել
// ԿԱՐԵՎՈՐ!
// OnTerminate իրադարձության մշակման մեթոդը միշտ կոչվում է հիմնականի համատեքստում
// թեմա - սա երաշխավորված է tThread իրականացման միջոցով: Հետեւաբար, դրանում կարող եք ազատորեն
// օգտագործել ցանկացած օբյեկտի ցանկացած հատկություն և մեթոդ

// Ամեն դեպքում, համոզվեք, որ օբյեկտի օրինակը դեռ գոյություն ունի
եթե նշանակված չէ (MyThread), ապա Ելք; // եթե այնտեղ չկա, ուրեմն անելիք չկա

// ստանալ թելի օբյեկտի օրինակի թելի աշխատանքի արդյունքները
Form1.Memo1.Lines.Add (Ձևաչափ («Հոսքը ավարտվեց արդյունքով% d»,));
Form1.Memo1.Lines.AddStrings ((ուղարկողը որպես tMyThread) .ResultList);

// Ոչնչացնել հոսքի օբյեկտի օրինակին հղումը:
// Քանի որ մեր շարանը ինքնաոչնչացվում է (FreeOnTerminate: = True)
// ապա OnTerminate կարգավորիչի ավարտից հետո հոսքի օբյեկտի օրինակը կլինի
// ոչնչացված (Անվճար) և դրան վերաբերող բոլոր հղումները անվավեր կդառնան:
// Որպեսզի պատահաբար չհանդիպեք նման հղման, վերագրեք MyThread- ը
// Ես մեկ անգամ ևս կնշեմ. Մենք չենք քանդի օբյեկտը, այլ միայն կգրենք հղումը: Օբյեկտ
// ոչնչացնել ինքն իրեն!
MyThread: = զրո;
վերջ;

կոնստրուկտոր tMyThread.Create (aParam1: String);
սկսել
// Ստեղծեք Կասեցված հոսքի օրինակ (տե՛ս մեկնաբանությունը, երբ դա հուշում է)
ժառանգել Ստեղծել (Trueշմարիտ);

// Ստեղծեք ներքին օբյեկտներ (անհրաժեշտության դեպքում)
ResultList: = tStringList.Create;

// Ստացեք նախնական տվյալներ:

// Պատճենեք պարամետրով փոխանցված մուտքային տվյալները
Param1: = aParam1;

// հոսքային օբյեկտի կոնստրուկտորում VCL բաղադրիչներից մուտքային տվյալների ստացման օրինակ
// Սա այս դեպքում ընդունելի է, քանի որ կոնստրուկտորը կոչվում է համատեքստում
// հիմնական թեմա: Հետևաբար, այստեղ կարելի է մուտք գործել VCL բաղադրիչներ:
// Բայց, սա ինձ դուր չի գալիս, քանի որ կարծում եմ, որ վատ է, երբ շարանը ինչ -որ բան գիտի
// այնտեղ ինչ -որ ձևի մասին: Բայց, ինչ չես կարող անել ցույցի համար:
Param3: = Form1.CheckBox1.Checked;
վերջ;

destructor tMyThread.Destroy;
սկսել
// ներքին օբյեկտների ոչնչացում
FreeAndNil (ResultList);
// ոչնչացնել հիմնական tThread- ը
ժառանգված;
վերջ;

ընթացակարգ tMyThread.Execute;
var
t: կարդինալ;
s: լարային;
սկսել
IterationNo: = 0; // արդյունքների հաշվիչ (ցիկլի համարը)

// Իմ օրինակում, թելի մարմինը հանգույց է, որն ավարտվում է
// կամ դադարեցման արտաքին «խնդրանքով», որն անցել է Stopped փոփոխական պարամետրով,
// կամ պարզապես 5 օղակ անելով
// Ինձ համար ավելի հաճելի է սա գրել «հավերժական» հանգույցի միջոցով:

Մինչ ճշմարիտը սկսվում է

Inc (IterationNo); // հաջորդ ցիկլի համարը

LastRandom: = Պատահական (1000); // առանցքային համար - ցույց տալ պարամետրերի փոխանցումը հոսքից ձևին

T: = Պատահական (5) +1; // ժամանակը, որի համար մենք կքնենք, եթե չավարտվենք

// Հիմար աշխատանք (կախված մուտքային պարամետրից)
եթե ոչ Param3, ուրեմն
Inc (Param2)
ուրիշ
Դեկտեմբեր (Param2);

// Ձևավորել միջանկյալ արդյունք
s: = Ձևաչափ ("% s% 5d% s% d% d",
);

// Արդյունքների ցանկին ավելացնել միջանկյալ արդյունք
ResultList.Add (ներ);

//// Միջանկյալ արդյունքը ձևին փոխանցելու օրինակներ

//// Համաժամեցված մեթոդով անցնելը `դասական եղանակը
//// Թերություններ.
//// - համաժամացման մեթոդը սովորաբար հոսքի դասի մեթոդ է (մուտքի համար
//// հոսքի օբյեկտի դաշտերին), բայց ձևի դաշտերին մուտք գործելու համար այն պետք է
//// «իմանալ» դրա և դրա դաշտերի (օբյեկտների) մասին, ինչը սովորաբար այնքան էլ լավ չէ
//// ծրագրի կազմակերպման տեսակետը:
//// - ընթացիկ շարանը կդադարեցվի մինչև կատարման ավարտը
//// համաժամեցված մեթոդ:

//// Առավելությունները.
//// - ստանդարտ և բազմակողմանի
//// - համաժամեցված մեթոդով կարող եք օգտագործել
//// հոսքի օբյեկտի բոլոր դաշտերը:
// նախ, անհրաժեշտության դեպքում, անհրաժեշտ է պահպանել փոխանցված տվյալները
// օբյեկտի օբյեկտի հատուկ դաշտեր:
SyncDataN: = IterationNo;
SyncDataS: = "Համաժամացում" + ներ;
// և այնուհետև տրամադրել համաժամեցված մեթոդի զանգ
Համաժամացնել (SyncMetod1);

//// Ուղարկելով համաժամանակյա հաղորդագրությունների ուղարկում (SendMessage)
//// այս դեպքում տվյալները կարող են փոխանցվել ինչպես հաղորդագրության պարամետրերով (LastRandom),
//// և օբյեկտի դաշտերով ՝ հաղորդագրության պարամետրում փոխանցելով ատյանի հասցեն
//// հոսքի օբյեկտի - Ամբողջ (Ես):
//// Թերություններ.
//// - շարանը պետք է իմանա ձևի պատուհանի բռնակը
//// - ինչպես Synchronize- ի դեպքում, ընթացիկ շարանը կդադարեցվի մինչև
//// ավարտելով հաղորդագրության մշակումը հիմնական թեմայով
//// - յուրաքանչյուր զանգի համար պահանջում է պրոցեսորի զգալի ժամանակ
//// (թեման փոխելու համար), հետևաբար, շատ հաճախակի զանգը անցանկալի է
//// Առավելությունները.
//// - ինչպես Synchronize- ի դեպքում, հաղորդագրություն մշակելիս կարող եք օգտագործել
//// հոսքի օբյեկտի բոլոր դաշտերը (եթե, իհարկե, նրա հասցեն փոխանցվել է)


//// սկսել շարանը:
SendMessage (Form1.Handle, WM_USER_SendMessageMetod, Integer (Self), LastRandom);

//// Փոխանցում ասինխրոն հաղորդագրությունների ուղարկմամբ (PostMessage)
//// Քանի որ այս դեպքում, մինչև հաղորդագրությունը ստանա հիմնական շարանը,
//// ուղարկող հոսքը կարող է արդեն ավարտվել ՝ փոխանցելով ատյանի հասցեն
//// հոսքի օբյեկտն անվավեր է:
//// Թերություններ.
//// - շարանը պետք է իմանա ձևի պատուհանի բռնակ;
//// - ասինխրոնիայի պատճառով տվյալների փոխանցումը հնարավոր է միայն պարամետրերի միջոցով
//// հաղորդագրություններ, ինչը զգալիորեն դժվարացնում է տվյալների փոխանցումը, որն ունի չափսեր
//// ավելի քան երկու մեքենայական բառ: Հարմար է օգտագործել ամբողջ թիվ փոխանցելու համար և այլն:
//// Առավելությունները.
//// - ի տարբերություն նախորդ մեթոդների, ընթացիկ շարանը ՉԻ
//// դադարեցված է և անմիջապես կվերսկսի կատարումը
//// - ի տարբերություն համաժամեցված զանգի, հաղորդագրությունների մշակող
//// ձևային մեթոդ է, որը պետք է ունենա հոսքի օբյեկտի մասին գիտելիքներ,
//// կամ ընդհանրապես ոչինչ չգիտեք հոսքի մասին, եթե տվյալները փոխանցվում են միայն
//// հաղորդագրության պարամետրերի միջոցով: Այսինքն, շարանը կարող է ոչինչ չգիտի ձևի մասին:
//// ընդհանրապես `միայն նրա Բռնակ, որը նախկինում կարող է փոխանցվել որպես պարամետր
//// սկսել շարանը:
PostMessage (Form1.Handle, WM_USER_PostMessageMetod, IterationNo, LastRandom);

//// Ստուգեք հնարավոր ավարտի համար

// Ստուգեք ավարտը ըստ պարամետրի
եթե Stopped ապա Break;

// Ստուգեք ավարտի առիթով
եթե IterationNo> = 10 ապա Break;

Քուն (t * 1000); // Քնել t վայրկյան
վերջ;
վերջ;

ընթացակարգ tMyThread.SyncMetod1;
սկսել
// այս մեթոդը կոչվում է Synchronize մեթոդի միջոցով:
// Այսինքն, չնայած այն հանգամանքին, որ դա tMyThread թելի մեթոդ է,
// այն աշխատում է հավելվածի հիմնական թելի համատեքստում:
// Հետեւաբար, նա կարող է անել ամեն ինչ, լավ, կամ գրեթե ամեն ինչ :)
// Բայց հիշեք, այստեղ չարժե երկար ժամանակ «խառնվել»

// Անցած պարամետրերը, մենք կարող ենք քաղել հատուկ դաշտերից, որտեղ մենք դրանք ունենք
// պահպանվել է զանգելուց առաջ:
Form1.Label1.Caption: = SyncDataS;

// հոսքի օբյեկտի այլ դաշտերից, օրինակ ՝ արտացոլելով դրա ներկա վիճակը
Form1.Label2.Caption: = Ձևաչափ ("% d% d",);
վերջ;

Ընդհանրապես, օրինակին նախորդել էր թեմայի վերաբերյալ իմ հետևյալ հիմնավորումը ...

Նախ ՝
Դելֆիում բազմաթել ծրագրավորման ամենակարևոր կանոնը հետևյալն է.
Ոչ հիմնական թեմայի համատեքստում դուք չեք կարող մուտք գործել ձևերի հատկությունները և մեթոդները, և իսկապես բոլոր այն բաղադրիչները, որոնք «աճում են» tWinControl- ից:

Սա նշանակում է (որոշ չափով պարզեցված), որ ո՛չ TThread- ից ժառանգված Գործարկման մեթոդով, ո՛չ էլ Գործադրումից կոչված այլ մեթոդներով / ընթացակարգերով / գործառույթներով, դա արգելված էուղղակիորեն մուտք գործել տեսողական բաղադրիչների ցանկացած հատկություններ և մեթոդներ:

Ինչպես դա անել ճիշտ:
Չկան միասնական բաղադրատոմսեր: Ավելի ճիշտ, կան այնքան շատ և տարբեր տարբերակներ, որոնք, կախված կոնկրետ դեպքից, անհրաժեշտ է ընտրել: Հետեւաբար, նրանք հղում են կատարում հոդվածին: Այն կարդալով և հասկանալով ՝ ծրագրավորողը կկարողանա հասկանալ և ինչպես լավագույնս դա անել կոնկրետ դեպքում:

Մի խոսքով մատների վրա.

Ամենից հաճախ բազմալեզու հավելվածը դառնում է կամ այն ​​ժամանակ, երբ անհրաժեշտ է ինչ-որ երկարաժամկետ աշխատանք կատարել, կամ երբ հնարավոր է միաժամանակ անել մի քանի բան, որոնք ծանրաբեռնված չեն պրոցեսորը:

Առաջին դեպքում, հիմնական շարանի ներսում աշխատանքի իրականացումը հանգեցնում է օգտագործողի միջերեսի «դանդաղեցմանը». Մինչ աշխատանքը կատարվում է, հաղորդագրությունների օղակը չի կատարվում: Արդյունքում, ծրագիրը չի արձագանքում օգտվողի գործողություններին, և ձևը չի գծվում, օրինակ ՝ օգտագործողի կողմից այն տեղափոխելուց հետո:

Երկրորդ դեպքում, երբ աշխատանքը ներառում է արտաքին աշխարհի հետ ակտիվ փոխանակում, ապա հարկադիր «պարապուրդի» ժամանակ: Տվյալների ստացման / ուղարկման սպասմանը զուգահեռ կարող եք զուգահեռաբար այլ բան անել, օրինակ ՝ կրկին, ուղարկել / ստանալ տվյալներ:

Կան այլ դեպքեր, բայց ավելի հազվադեպ: Այնուամենայնիվ, դա նշանակություն չունի: Հիմա դրա մասին չէ:

Հիմա, ինչպես է ամեն ինչ գրված: Բնականաբար, դիտարկվում է որոշակի ամենահաճախակի, որոշ չափով ընդհանրացված դեպք: Այսպիսով,

Առանձին թեմայով իրականացվող աշխատանքը, ընդհանուր դեպքում, ունի չորս սուբյեկտ (չգիտեմ ինչպես դա ավելի ճշգրիտ անվանել).
1. Նախնական տվյալներ
2. Իրականում աշխատանքն ինքնին (այն կարող է կախված լինել սկզբնական տվյալներից)
3. Միջանկյալ տվյալներ (օրինակ ՝ աշխատանքի կատարման ընթացիկ վիճակի մասին տեղեկություններ)
4. Ելքային տվյալներ (արդյունք)

Ամենից հաճախ տեսողական բաղադրիչներն օգտագործվում են տվյալների մեծ մասը կարդալու և ցուցադրելու համար: Բայց, ինչպես նշվեց վերևում, հոսքից ուղղակիորեն չեք կարող մուտք գործել տեսողական բաղադրիչներ: Ինչպե՞ս լինել:
Delphi- ի մշակողները առաջարկում են օգտագործել TThread դասի Synchronize մեթոդը: Այստեղ ես չեմ նկարագրելու, թե ինչպես օգտագործել այն. Դրա համար կա վերը նշված հոդվածը: Միայն ասեմ, որ դրա կիրառումը, նույնիսկ ճիշտը, միշտ չէ, որ արդարացված է: Երկու խնդիր կա.

Նախ, Synchronize- ի միջոցով կոչվող մեթոդի մարմինը միշտ կատարվում է հիմնական շարանի համատեքստում, և, հետևաբար, մինչ այն կատարվում է, կրկին պատուհանի հաղորդագրությունների օղակը չի կատարվում: Հետևաբար, այն պետք է արագ կատարվի, հակառակ դեպքում մենք կստանանք նույն խնդիրները, ինչ մեկ թելերով իրականացումը: Իդեալում, Synchronize- ի միջոցով կոչվող մեթոդը, ընդհանուր առմամբ, պետք է օգտագործվի միայն տեսողական օբյեկտների հատկություններին և մեթոդներին մուտք գործելու համար:

Երկրորդ, Synchronize- ի միջոցով մեթոդի կիրառումը «թանկ» հաճույք է ՝ թելերի միջև երկու անջատիչների անհրաժեշտության պատճառով:

Ավելին, երկու խնդիրներն էլ փոխկապակցված են և հակասություն են առաջացնում. Մի կողմից ՝ առաջինը լուծելու համար անհրաժեշտ է «մանրացնել» Synchronize- ի միջոցով կոչված մեթոդները, իսկ մյուս կողմից ՝ դրանք հաճախ ստիպված են կանչվել ՝ կորցնելով թանկարժեք պրոցեսորային ռեսուրսներ:

Հետևաբար, ինչպես միշտ, անհրաժեշտ է ողջամիտ մոտենալ, և տարբեր դեպքերում օգտագործել արտաքին աշխարհի հետ հոսքի փոխազդեցության տարբեր եղանակներ.

Նախնական տվյալներ
Բոլոր տվյալները, որոնք փոխանցվում են հոսքին և չեն փոխվում դրա գործունեության ընթացքում, պետք է փոխանցվեն նույնիսկ դրա մեկնարկից առաջ, այսինքն. հոսք ստեղծելիս: Թելի մարմնում դրանք օգտագործելու համար հարկավոր է դրանց տեղական պատճենը պատրաստել (սովորաբար TThread- ի ժառանգի դաշտերում):
Եթե ​​կան նախնական տվյալներ, որոնք կարող են փոխվել շարանը գործարկելիս, ապա այդ տվյալները պետք է հասանելի լինեն կամ համաժամանակացված մեթոդներով (մեթոդներ, որոնք կոչվում են Synchronize), կամ թելի օբյեկտի դաշտերի միջոցով (TThread- ի ժառանգ): Վերջինս որոշակի զգուշություն է պահանջում:

Միջանկյալ և ելքային տվյալներ
Այստեղ կրկին կան մի քանի եղանակներ (ըստ իմ նախընտրության).
- Հաղորդագրությունների հիմնական պատուհանին հաղորդագրությունների ասինխրոն ուղարկման եղանակ:
Այն սովորաբար օգտագործվում է գործընթացի առաջընթացի մասին հաղորդագրություններ ուղարկելու հիմնական ծրագրի պատուհան ՝ փոքր քանակությամբ տվյալների փոխանցմամբ (օրինակ ՝ ավարտի տոկոսը)
- Հաղորդագրությունների հիմնական պատուհանին հաղորդագրություններ համաժամանակորեն ուղարկելու եղանակ:
Սովորաբար օգտագործվում է նույն նպատակներով, ինչ ասինխրոն ուղարկելը, սակայն թույլ է տալիս փոխանցել ավելի մեծ քանակությամբ տվյալներ ՝ առանց առանձին պատճեն ստեղծելու:
- Հնարավորության դեպքում համաժամեցված մեթոդներ ՝ հնարավորինս շատ տվյալների փոխանցումը համատեղելով մեկ մեթոդի մեջ:
Կարող է օգտագործվել նաև ձևից տվյալները վերցնելու համար:
- հոսքի օբյեկտի դաշտերի միջոցով ՝ ապահովելով փոխադարձ բացառիկ մուտք:
Ավելի մանրամասն կարելի է գտնել հոդվածում:

Էհ Կարճ ժամանակ չստացվեց