რა მიზნებისთვის გამოიყენება მრავალსიდიანი სისტემები. რვა მარტივი წესი მრავალპროფილიანი პროგრამების შემუშავებისათვის

რომელი თემა აჩენს ყველაზე მეტ კითხვას და სირთულეს დამწყებთათვის? როდესაც ამის შესახებ ვკითხე ჩემს მასწავლებელს და ჯავის პროგრამისტს ალექსანდრე პრიახინს, მან მაშინვე მიპასუხა: "მრავალსიტყვაობა". მადლობა მას იდეისათვის და ამ სტატიის მომზადებაში დახმარებისთვის!

ჩვენ გადავხედავთ პროგრამის შინაგან სამყაროს და მის პროცესებს, გავარკვევთ რა არის მრავალსიდიანი არხის არსი, როდის არის ის სასარგებლო და როგორ განვახორციელოთ იგი - ჯავის მაგალითის გამოყენებით. თუ თქვენ სწავლობთ სხვა OOP ენას, არ ინერვიულოთ: ძირითადი პრინციპები იგივეა.

ნაკადების და მათი წარმოშობის შესახებ

მრავალმხრივი კითხვის გასაგებად, ჯერ გავიგოთ რა არის პროცესი. პროცესი არის ვირტუალური მეხსიერების ნაწილი და რესურსი, რომელსაც OS გამოყოფს პროგრამის გასაშვებად. თუ გახსნით ერთი და იმავე პროგრამის რამდენიმე მაგალითს, სისტემა გამოყოფს პროცესს თითოეულისთვის. თანამედროვე ბრაუზერებში ცალკე ჩანართი შეიძლება იყოს პასუხისმგებელი თითოეულ ჩანართზე.

თქვენ ალბათ წააწყდით Windows- ის "სამუშაო მენეჯერს" (Linux- ში არის "სისტემის მონიტორი") და თქვენ იცით, რომ არასაჭირო გაშვებული პროცესები იტვირთება სისტემაში და მათგან ყველაზე "მძიმე" ხშირად იყინება, ამიტომ ისინი იძულებით უნდა შეწყდეს რა

მაგრამ მომხმარებლებს უყვართ მრავალფუნქციური ამოცანა: ნუ აჭმევთ მათ პურით - ნება მიეცით გახსნან ათეული ფანჯარა და გადახტნენ წინ და უკან. არსებობს დილემა: თქვენ უნდა უზრუნველყოთ პროგრამების ერთდროული მოქმედება და ამავდროულად შეამციროთ სისტემაზე დატვირთვა ისე, რომ ის არ შენელდეს. ვთქვათ, აპარატურა ვერ აკმაყოფილებს მფლობელების საჭიროებებს - თქვენ უნდა გადაწყვიტოთ საკითხი პროგრამული უზრუნველყოფის დონეზე.

ჩვენ გვინდა, რომ პროცესორმა შეასრულოს მეტი ინსტრუქცია და დაამუშაოს მეტი მონაცემი დროის ერთეულში. ანუ, ჩვენ უნდა მოვათავსოთ მეტი შესრულებული კოდი ყოველ ჯერზე. იფიქრეთ კოდის შესრულების ერთეულზე, როგორც ობიექტზე - ეს არის ძაფი.

რთული შემთხვევის მიდგომა უფრო ადვილია, თუ მას რამდენიმე მარტივ პირად დაყოფთ. ასე ხდება მეხსიერებასთან მუშაობისას: "მძიმე" პროცესი დაყოფილია ძაფებად, რომლებიც იკავებენ ნაკლებ რესურსს და უფრო სავარაუდოა, რომ მიაწოდონ კოდი კალკულატორს (რამდენად ზუსტად - იხილეთ ქვემოთ).

თითოეულ აპლიკაციას აქვს მინიმუმ ერთი პროცესი და თითოეულ პროცესს აქვს მინიმუმ ერთი ძაფი, რომელსაც ეწოდება მთავარი ძაფი და საიდანაც საჭიროების შემთხვევაში იწყება ახალი.

განსხვავება ძაფებსა და პროცესებს შორის

    ძაფები იყენებენ პროცესისთვის გამოყოფილ მეხსიერებას და პროცესები მოითხოვს მათ მეხსიერების ადგილს. ამიტომ, ძაფები იქმნება და სრულდება უფრო სწრაფად: სისტემას არ სჭირდება ყოველ ჯერზე გამოყოს მათთვის ახალი მისამართის სივრცე და შემდეგ გაათავისუფლოს იგი.

    თითოეული ამუშავებს საკუთარ მონაცემებს - მათ შეუძლიათ რაღაცის გაცვლა მხოლოდ ინტერპროცესული კომუნიკაციის მექანიზმის საშუალებით. თემები ერთმანეთის მონაცემებსა და რესურსებს უშუალოდ წვდებიან: ის რაც შეიცვალა დაუყოვნებლივ ხელმისაწვდომია ყველასთვის. ძაფს შეუძლია გააკონტროლოს "თანამემამულე" პროცესში, ხოლო პროცესი აკონტროლებს ექსკლუზიურად მის "ქალიშვილებს". ამიტომ, ნაკადებს შორის გადართვა უფრო სწრაფია და მათ შორის კომუნიკაცია უფრო ადვილია.

რა არის აქედან დასკვნა? თუ თქვენ გჭირდებათ რაც შეიძლება სწრაფად დიდი რაოდენობის მონაცემების დამუშავება, გაყავით ის ნაწილებად, რომელთა დამუშავებაც შესაძლებელია ცალკეული ძაფებით, შემდეგ კი შედეგი ერთად გაანაწილეთ. ეს უკეთესია, ვიდრე რესურსზე მშიერი პროცესების წარმოქმნა.

მაგრამ რატომ მიდის Firefox– ის მსგავსი აპლიკაცია მრავალი პროცესის შექმნის გზაზე? რადგან ბრაუზერისთვის იზოლირებული ჩანართების მუშაობა საიმედო და მოქნილია. თუ რაღაც პროცესში რაღაც არ არის სწორი, არ არის აუცილებელი მთელი პროგრამის შეწყვეტა - შესაძლებელია მონაცემების ნაწილის შენახვა მაინც.

რა არის მრავალსიდიანი

ასე რომ, ჩვენ მივედით მთავარ საკითხამდე. მრავალსიდიანი არის, როდესაც განაცხადის პროცესი იყოფა ძაფებად, რომლებიც დამუშავებულია პარალელურად - დროის ერთ ერთეულზე - პროცესორის მიერ.

გამოთვლითი დატვირთვა ნაწილდება ორ ან მეტ ბირთვს შორის, ისე რომ ინტერფეისი და პროგრამის სხვა კომპონენტები არ შეანელებენ ერთმანეთის მუშაობას.

მრავალ ხრახნიანი პროგრამების გაშვება ასევე შესაძლებელია ერთ ბირთვიან პროცესორებზე, მაგრამ შემდეგ ძაფები თავის მხრივ ხდება: პირველი მუშაობდა, მისი მდგომარეობა შენახული იყო - მეორეს მუშაობის უფლება მიეცა, შეინახა - დაუბრუნდა პირველს ან გაუშვა მესამე, და ა.შ.

დაკავებული ხალხი ჩივის, რომ მათ მხოლოდ ორი ხელი აქვთ. პროცესებსა და პროგრამებს შეიძლება ჰქონდეთ იმდენი ხელი, რამდენიც საჭიროა დავალების რაც შეიძლება სწრაფად შესასრულებლად.

დაელოდეთ სიგნალს: სინქრონიზაცია მრავალ ხრახნიან პროგრამებში

წარმოიდგინეთ, რომ რამდენიმე თემა ცდილობს შეცვალოს ერთიდაიგივე მონაცემთა არე ერთდროულად. ვისი ცვლილებები იქნება საბოლოოდ მიღებული და ვისი ცვლილებები გაუქმდება? გაუგებრობის თავიდან ასაცილებლად საერთო რესურსებთან ურთიერთობისას, ძაფებმა უნდა მოახდინონ მათი მოქმედებების კოორდინირება. ამისათვის ისინი გაცვლიან ინფორმაციას სიგნალების გამოყენებით. თითოეული თემა ეუბნება სხვებს რას აკეთებს და რა ცვლილებები ელის. ასე რომ, ყველა თემის მონაცემები რესურსების ამჟამინდელი მდგომარეობის შესახებ სინქრონიზებულია.

სინქრონიზაციის ძირითადი ინსტრუმენტები

Საერთო გამონაკლისი (ურთიერთგამომრიცხავი, შემოკლებით - mutex) - "დროშა", რომელიც მიდის იმ თემაში, რომელსაც ამჟამად ნებადართულია იმუშაოს საერთო რესურსებით. გამორიცხავს სხვა ძაფებით წვდომას ოკუპირებული მეხსიერების არეზე. შეიძლება იყოს რამდენიმე მუტექსი პროგრამაში და მათი გაზიარება შესაძლებელია პროცესებს შორის. არის პრობლემა: mutex აიძულებს პროგრამას ყოველ ჯერზე შევიდეს ოპერაციული სისტემის ბირთვში, რაც ძვირია.

სემაფორი - საშუალებას გაძლევთ შეზღუდოთ ძაფების რაოდენობა, რომლებსაც შეუძლიათ რესურსზე წვდომა მოცემულ მომენტში. ეს შეამცირებს დატვირთვას პროცესორზე კოდის შესრულებისას, სადაც არის შეფერხებები. პრობლემა ისაა, რომ ძაფების ოპტიმალური რაოდენობა დამოკიდებულია მომხმარებლის აპარატზე.

ღონისძიება - თქვენ განსაზღვრავთ მდგომარეობას, რომლის გაჩენისთანავე კონტროლი გადადის სასურველ ძაფზე. ნაკადები გაცვლიან მოვლენების მონაცემებს, რათა განავითარონ და ლოგიკურად გააგრძელონ ერთმანეთის ქმედებები. ერთმა მიიღო მონაცემები, მეორემ შეამოწმა მისი სისწორე, მესამემ შეინახა მყარ დისკზე. მოვლენები განსხვავდება მათი გაუქმების ფორმით. თუ თქვენ გჭირდებათ რამდენიმე თემის შეტყობინება მოვლენის შესახებ, თქვენ მოგიწევთ ხელით დააყენოთ გაუქმების ფუნქცია სიგნალის შესაჩერებლად. თუ არსებობს მხოლოდ ერთი სამიზნე ძაფი, შეგიძლიათ შექმნათ ავტომატური გადატვირთვის ღონისძიება. ის შეწყვეტს სიგნალს თავად მას შემდეგ რაც მიაღწევს ნაკადს. მოვლენების მორიგეობა შესაძლებელია მოქნილი ნაკადის კონტროლისთვის.

კრიტიკული განყოფილება - უფრო რთული მექანიზმი, რომელიც აერთიანებს მარყუჟის მრიცხველს და სემაფორს. მრიცხველი საშუალებას გაძლევთ გადადოთ სემფორის დაწყება სასურველ დროს. უპირატესობა ისაა, რომ ბირთვი გააქტიურებულია მხოლოდ იმ შემთხვევაში, თუ მონაკვეთი დაკავებულია და სემიფორმის ჩართვაა საჭირო. დანარჩენ დროს, თემა მუშაობს მომხმარებლის რეჟიმში. სამწუხაროდ, განყოფილება შეიძლება გამოყენებულ იქნას მხოლოდ ერთ პროცესში.

როგორ განვახორციელოთ მულტიტრედი ჯავაში

თემა კლასი პასუხისმგებელია ჯავაზე ძაფებთან მუშაობაზე. დავალების შესასრულებლად ახალი ძაფის შექმნა ნიშნავს თემის კლასის მაგალითის შექმნას და თქვენთვის სასურველ კოდთან ასოცირებას. ეს შეიძლება გაკეთდეს ორი გზით:

    ქვეკლასი თემა;

    განახორციელეთ Runnable ინტერფეისი თქვენს კლასში და შემდეგ გადასცეს კლასის მაგალითები თემის კონსტრუქტორს.

მიუხედავად იმისა, რომ ჩვენ არ შევეხებით ჩიხების თემას, როდესაც ძაფები ბლოკავს ერთმანეთის მუშაობას და იყინება, ჩვენ ამას დავტოვებთ შემდეგი სტატიისათვის.

ჯავის მრავალსიდიანი მაგალითი: პინგ-პონგი მუტექსებით

თუ ფიქრობთ, რომ რაღაც საშინელება მოხდება, ამოისუნთქეთ. ჩვენ განვიხილავთ სინქრონიზაციის ობიექტებთან მუშაობას თითქმის სათამაშოდ: ორ ძაფს მოექცევა მუტექსი. მაგრამ სინამდვილეში თქვენ ნახავთ რეალურ პროგრამას, სადაც მხოლოდ ერთ ძაფს შეუძლია საჯაროდ ხელმისაწვდომი მონაცემების დამუშავება ერთდროულად.

პირველი, მოდით შევქმნათ კლასი, რომელიც მემკვიდრეობით იღებს უკვე ნაცნობი თემის თვისებებს და დავწეროთ kickBall მეთოდი:

საჯარო კლასი PingPongThread ავრცელებს თემას (PingPongThread (სიმებიანი სახელი) (this.setName (სახელი); // გადალახავს ძაფის სახელს) @Override public void run () (Ball ball = Ball.getBall (); while (ball.isInGame () ) (kickBall (ბურთი);)) პირადი ბათილი kickBall (ბურთი ბურთი) (თუ (! ball.getSide (). უდრის (getName ())) (ball.kick (მიიღეთ სახელი ());))))

ახლა მოდით ვიზრუნოთ ბურთზე. ის ჩვენთან არ იქნება უბრალო, არამედ დასამახსოვრებელი: რათა მან თქვას ვინ დაარტყა, რომელი მხრიდან და რამდენჯერ. ამისათვის ჩვენ ვიყენებთ მუტექსს: ის შეაგროვებს ინფორმაციას თითოეული ძაფის მუშაობის შესახებ - ეს საშუალებას მისცემს იზოლირებულ ძაფებს ერთმანეთთან ურთიერთობა. მე -15 დარტყმის შემდეგ, ჩვენ გამოვიყვანთ ბურთს თამაშიდან, რათა სერიოზულად არ დავაზიანოთ იგი.

საჯარო კლასის ბურთი (კერძო int დარტყმები = 0; კერძო სტატიკური ბურთის მაგალითი = ახალი ბურთი), (დარტყმები ++; მხარე = სათამაშო სახელი; System.out.println (დარტყმები + "" + გვერდი);) String getSide () (დაბრუნების მხარე;) ლოგიკური isInGame () (დაბრუნება (დარტყმები< 15); } }

ახლა კი ორი მოთამაშის ძაფი შემოდის სცენაზე. მოდით დავურეკოთ მათ, ყოველგვარი აჟიოტაჟის გარეშე, პინგი და პონგი:

საჯარო კლასი PingPongGame (PingPongThread player1 = ახალი PingPongThread ("პინგი"); PingPongThread მოთამაშე 2 = ახალი PingPongThread ("პონგი"); ბურთის ბურთი; PingPongGame () (ბურთი = Ball.getBall ();) ბათილად დაწყებული თამაში () აგდებს მოთამაშეს InterceptionException . დაწყება (); მოთამაშე 2. დაწყება ();))

"ხალხის სრული სტადიონი - დროა დაიწყოს მატჩი." ჩვენ ოფიციალურად გამოვაცხადებთ შეხვედრის გახსნას - განაცხადის ძირითად კლასში:

საჯარო კლასი PingPong (საჯარო სტატიკური ბათილი მთავარი (სიმებიანი არგები) ისვრის InterruptException (PingPongGame თამაში = ახალი PingPongGame (); game.startGame ();))

როგორც ხედავთ, აქ აღშფოთებული არაფერია. ეს ჯერ მხოლოდ შესავალია მულტიტრედინში, მაგრამ თქვენ უკვე იცით როგორ მუშაობს იგი და შეგიძლიათ ექსპერიმენტი ჩაატაროთ - შეზღუდეთ თამაშის ხანგრძლივობა არა დარტყმების რაოდენობით, არამედ დროით, მაგალითად. ჩვენ მოგვიანებით დავუბრუნდებით მრავალსიდიანი თემის თემას - ჩვენ გადავხედავთ java.util.concurrent პაკეტს, Akka ბიბლიოთეკას და არასტაბილურ მექანიზმს. მოდით ასევე ვისაუბროთ პითონში მულტიტრედის განხორციელებაზე.

მრავალსიდიანი პროგრამირება ფუნდამენტურად არ განსხვავდება მოვლენებზე ორიენტირებული გრაფიკული მომხმარებლის ინტერფეისის წერისგან ან თუნდაც მარტივი თანმიმდევრული პროგრამების წერისგან. აქ გამოიყენება ყველა მნიშვნელოვანი წესი, რომელიც არეგულირებს კაფსულაციას, შეშფოთების გამიჯვნას, ფხვიერ დაწყვილებას და ა.შ. მაგრამ ბევრ დეველოპერს უჭირს მრავალსართულიანი პროგრამების წერა ზუსტად იმიტომ, რომ ისინი უგულებელყოფენ ამ წესებს. სამაგიეროდ, ისინი ცდილობენ პრაქტიკაში გამოიყენონ გაცილებით ნაკლებად მნიშვნელოვანი ცოდნა ძაფებისა და პრიმიტივების სინქრონიზაციის შესახებ, რომლებიც ამოღებულია დამწყებთათვის მრავალსიდიანი პროგრამირების ტექსტებიდან.

მაშ რა არის ეს წესები

სხვა პროგრამისტი, პრობლემის წინაშე, ფიქრობს: "ოჰ, ზუსტად, ჩვენ უნდა გამოვიყენოთ რეგულარული გამონათქვამები." ახლა კი მას უკვე აქვს ორი პრობლემა - ჯემი ზავინსკი.

სხვა პროგრამისტი, პრობლემის წინაშე, ფიქრობს: "ოჰ, მართალია, მე გამოვიყენებ ნაკადებს აქ." ახლა კი მას ათი პრობლემა აქვს - ბილ შინდლერი.

ძალიან ბევრი პროგრამისტი, რომლებიც ვალდებულებას იღებენ დაწერონ მრავალძირიანი კოდი, ხაფანგში ხვდებიან, როგორც გოეთეს ბალადის გმირი " ჯადოქრის შეგირდი". პროგრამისტი შეისწავლის თუ როგორ უნდა შექმნას რამოდენიმე ძაფი, რომელიც, პრინციპში, მუშაობს, მაგრამ ადრე თუ გვიან ისინი კონტროლიდან გამოდიან და პროგრამისტმა არ იცის რა უნდა გააკეთოს.

ჯადოქრებისგან განთავისუფლებისგან განსხვავებით, უიღბლო პროგრამისტს არ შეუძლია იმედი ჰქონდეს მძლავრი ჯადოქრის მოსვლას, რომელიც კვერთხს აიქნევს და წესრიგს აღადგენს. სამაგიეროდ, პროგრამისტი მიდის ყველაზე უსიამოვნო ხრიკებზე, ცდილობს გაუმკლავდეს მუდმივად წარმოქმნილ პრობლემებს. შედეგი ყოველთვის ერთი და იგივეა: მიიღება ზედმეტად რთული, შეზღუდული, მყიფე და არასაიმედო პროგრამა. მას აქვს მუდმივი ჩიხი და სხვა საფრთხეები, რომლებიც თან ახლავს ცუდ მრავალმხრივ კოდს. მე არც კი ვსაუბრობ აუხსნელი ავარიების, ცუდი შესრულების, არასრული ან არასწორი მუშაობის შედეგების შესახებ.

თქვენ ალბათ გაინტერესებთ: რატომ ხდება ეს? გავრცელებული მცდარი მოსაზრებაა: "მრავალძირიანი პროგრამირება ძალიან რთულია". მაგრამ ეს ასე არ არის. თუ მრავალ ხრახნიანი პროგრამა არასაიმედოა, მაშინ ის, როგორც წესი, ვერ ხერხდება იმავე მიზეზების გამო, როგორც დაბალი ხარისხის ერთი ხრახნიანი პროგრამა. უბრალოდ, პროგრამისტი არ იცავს განვითარების ფუნდამენტურ, ცნობილ და დადასტურებულ მეთოდებს. როგორც ჩანს, მრავალსართულიანი პროგრამები უფრო რთულია, რადგან რაც უფრო მეტი პარალელური ძაფი იშლება, მით უფრო მეტი არეულობა ხდება - და ბევრად უფრო სწრაფად, ვიდრე ერთი თემა.

მცდარი წარმოდგენა "მრავალპროფილიანი პროგრამირების სირთულის შესახებ" ფართოდ გავრცელდა იმ დეველოპერების წყალობით, რომლებიც პროფესიონალურად განვითარდნენ ერთ ხრახნიანი კოდის წერისას, პირველად შეხვდნენ მრავალსიდიანს და არ გაუმკლავდნენ მას. იმის ნაცვლად, რომ გადახედონ თავიანთ მიკერძოებულობას და მუშაობის ჩვევებს, ისინი ჯიუტად აფიქსირებენ იმ ფაქტს, რომ მათ არ სურთ რაიმე ფორმით მუშაობა. არასანდო პროგრამული უზრუნველყოფისა და გამოტოვებული ვადების საბაბით, ეს ადამიანები ერთსა და იმავეს იმეორებენ: "მრავალსიდიანი პროგრამირება ძალიან რთულია".

გთხოვთ გაითვალისწინოთ, რომ ზემოთ მე ვსაუბრობ ტიპიურ პროგრამებზე, რომლებიც იყენებენ მულტიტრედინს. მართლაც, არსებობს კომპლექსური მრავალფუნქციური სცენარი-ისევე როგორც რთული ერთჯერადი. მაგრამ ისინი არ არიან გავრცელებული. როგორც წესი, პრაქტიკაში პროგრამისტისგან ზებუნებრივი არაფერია საჭირო. ჩვენ გადავიტანთ მონაცემებს, ვცვლით მას, ვაკეთებთ პერიოდულ გამოთვლებს და, ბოლოს, ვინახავთ ინფორმაციას მონაცემთა ბაზაში ან ვაჩვენებთ ეკრანზე.

არაფერია რთული ერთჯერადი ხრახნიანი პროგრამის საშუალო დონის გაუმჯობესებასა და მრავალფრთიან პროგრამაში გადაქცევაში. ყოველ შემთხვევაში არ უნდა იყოს. სირთულეები წარმოიქმნება ორი მიზეზის გამო:

  • პროგრამისტებმა არ იციან როგორ გამოიყენონ განვითარების მარტივი, კარგად ცნობილი აპრობირებული მეთოდები;
  • წიგნებში წარმოდგენილი ინფორმაციის უმეტესობა მრავალძირიანი პროგრამირების შესახებ არის ტექნიკურად სწორი, მაგრამ სრულიად გამოუყენებელი გამოყენებითი პრობლემების გადასაჭრელად.

პროგრამირების ყველაზე მნიშვნელოვანი კონცეფცია უნივერსალურია. ისინი თანაბრად ვრცელდება ერთ ხრახნიან და მრავალ ხრახნიან პროგრამებზე. ნაკადების მორევში ჩაძირულმა პროგრამისტებმა უბრალოდ არ ისწავლეს მნიშვნელოვანი გაკვეთილები, როდესაც დაეუფლნენ ერთ ხრახნიან კოდს. მე შემიძლია ვთქვა, რადგან ასეთი დეველოპერები უშვებენ ერთსა და იმავე ფუნდამენტურ შეცდომებს მრავალხაზოვან და ერთხაზოვან პროგრამებში.

ალბათ ყველაზე მნიშვნელოვანი გაკვეთილი, რომელიც უნდა ვისწავლოთ პროგრამის ისტორიის სამოცი წლის განმავლობაში: გლობალური ცვალებადი სახელმწიფო- ბოროტი... ნამდვილი ბოროტება. პროგრამები, რომლებიც ეყრდნობა გლობალურად ცვალებად მდგომარეობას, შედარებით ძნელია მსჯელობა და ზოგადად არასაიმედო, რადგანაც ძალიან ბევრი გზაა სახელმწიფოს შესაცვლელად. ბევრი კვლევა ჩატარდა, რომელიც ადასტურებს ამ ზოგად პრინციპს, არსებობს უამრავი დიზაინის შაბლონი, რომელთა მთავარი მიზანია მონაცემთა დამალვის ამა თუ იმ გზით განხორციელება. იმისათვის, რომ თქვენი პროგრამები უფრო პროგნოზირებადი იყოს, შეეცადეთ მაქსიმალურად აღმოფხვრათ ცვალებადი მდგომარეობა.

ერთ ხრახნიან სერიულ პროგრამაში მონაცემების გაფუჭების ალბათობა პირდაპირპროპორციულია იმ კომპონენტების რაოდენობასთან, რომლებსაც შეუძლიათ მონაცემების შეცვლა.

როგორც წესი, შეუძლებელია გლობალური სახელმწიფოს მთლიანად მოშორება, მაგრამ დეველოპერს აქვს არსენალში ძალიან ეფექტური ინსტრუმენტები, რაც საშუალებას გაძლევთ მკაცრად გააკონტროლოთ რომელი პროგრამის კომპონენტებს შეუძლიათ შეცვალონ მდგომარეობა. გარდა ამისა, ჩვენ ვისწავლეთ როგორ შევქმნათ შეზღუდული API ფენები პრიმიტიული მონაცემთა სტრუქტურების გარშემო. აქედან გამომდინარე, ჩვენ გვაქვს კარგი კონტროლი, თუ როგორ იცვლება ეს მონაცემთა სტრუქტურები.

გლობალურად ცვალებადი მდგომარეობის პრობლემები თანდათანობით გამოჩნდა 80-იანი წლების ბოლოს და 90-იანი წლების დასაწყისში, მოვლენებით გამოწვეული პროგრამირების გავრცელებით. პროგრამები აღარ იწყებოდა "თავიდან" და არ გადიოდა შესრულების ერთი, პროგნოზირებადი გზით "ბოლომდე". თანამედროვე პროგრამებს აქვთ საწყისი მდგომარეობა, რის შემდეგაც ხდება მოვლენები მათში - არაპროგნოზირებადი თანმიმდევრობით, ცვალებადი დროის ინტერვალებით. კოდი რჩება ერთი ხრახნიანი, მაგრამ უკვე ხდება ასინქრონული. მონაცემთა კორუფციის ალბათობა იზრდება ზუსტად იმიტომ, რომ მოვლენათა დადგომის რიგი ძალიან მნიშვნელოვანია. ამგვარი სიტუაციები საკმაოდ ხშირია: თუ B მოვლენა ხდება A მოვლენის შემდეგ, მაშინ ყველაფერი კარგად მუშაობს. მაგრამ თუ მოვლენა A ხდება B მოვლენის შემდეგ და C მოვლენას აქვს დრო, რომ ჩაერიოს მათ შორის, მაშინ მონაცემები შეიძლება დამახინჯდეს აღიარების მიღმა.

თუ პარალელური ნაკადებია ჩართული, პრობლემა კიდევ უფრო გამწვავდება, ვინაიდან რამდენიმე მეთოდი ერთდროულად შეიძლება მოქმედებდეს გლობალურ მდგომარეობაზე. შეუძლებელია ვიმსჯელოთ, რამდენად ზუსტად იცვლება გლობალური სახელმწიფო. ჩვენ უკვე ვსაუბრობთ არა მხოლოდ იმაზე, რომ მოვლენები შეიძლება მოხდეს არაპროგნოზირებადი თანმიმდევრობით, არამედ იმაზეც, რომ აღსრულების რამდენიმე ძაფის მდგომარეობა შეიძლება განახლდეს. ერთდროულად... ასინქრონული პროგრამირებით, თქვენ შეგიძლიათ, მინიმუმ, დარწმუნდეთ, რომ გარკვეული მოვლენა არ მოხდება სხვა მოვლენის დამუშავების დასრულებამდე. ანუ, დარწმუნებით შეიძლება ითქვას, როგორი იქნება გლობალური მდგომარეობა კონკრეტული მოვლენის დამუშავების ბოლოს. მრავალ ხრახნიან კოდში, როგორც წესი, შეუძლებელია იმის თქმა, თუ რომელი მოვლენები მოხდება პარალელურად, ამიტომ შეუძლებელია გლობალური მდგომარეობის გარკვევით აღწერა ნებისმიერ დროს.

მრავალმხრივი პროგრამა გლობალურად ცვალებადი მდგომარეობით არის ჰაიზენბერგის გაურკვევლობის პრინციპის ერთ -ერთი ყველაზე მჭევრმეტყველი მაგალითი, რომელიც მე ვიცი. შეუძლებელია პროგრამის მდგომარეობის შემოწმება მისი ქცევის შეცვლის გარეშე.

როდესაც ვიწყებ სხვა ფილიპეკას გლობალური ცვალებადი მდგომარეობის შესახებ (არსი ხაზგასმულია წინა რამდენიმე პარაგრაფში), პროგრამისტები თვალებს ატრიალებენ და დამარწმუნებენ, რომ მათ ეს ყველაფერი დიდი ხანია იციან. მაგრამ თუ თქვენ იცით ეს, რატომ არ შეგიძლიათ თქვათ თქვენი კოდიდან? პროგრამები სავსეა გლობალური ცვალებადი მდგომარეობით და პროგრამისტებს აინტერესებთ, რატომ არ მუშაობს კოდი.

გასაკვირი არ არის, რომ მრავალფუნქციური პროგრამირების ყველაზე მნიშვნელოვანი სამუშაო ხდება დიზაინის ფაზაში. საჭიროა მკაფიოდ განისაზღვროს რა უნდა გააკეთოს პროგრამამ, განავითაროს დამოუკიდებელი მოდულები ყველა ფუნქციის შესასრულებლად, დეტალურად აღწეროს რა მონაცემია საჭირო რომელი მოდულისთვის და განსაზღვროს მოდულებს შორის ინფორმაციის გაცვლის გზები ( დიახ, არ დაგავიწყდეთ ლამაზი მაისურების მომზადება ყველასთვის, ვინც მონაწილეობს პროექტში. Პირველი რამ.- დაახლოებით ედ. ორიგინალში). ეს პროცესი ფუნდამენტურად არ განსხვავდება ერთი ხრახნიანი პროგრამის შემუშავებისაგან. წარმატების გასაღები, ისევე როგორც ერთი ხრახნიანი კოდი, არის მოდულებს შორის ურთიერთქმედების შეზღუდვა. თუ თქვენ შეგიძლიათ თავი დაეღწია საერთო მუტაბელურ მდგომარეობას, მონაცემთა გაზიარების პრობლემები უბრალოდ არ წარმოიქმნება.

ვიღაცამ შეიძლება ამტკიცოს, რომ ზოგჯერ დრო არ არის პროგრამის ისეთი დელიკატური დიზაინისთვის, რაც შესაძლებელს გახდის გლობალური სახელმწიფოს გარეშე. მე მჯერა, რომ შესაძლებელია და აუცილებელია ამაზე დროის დახარჯვა. არაფერი გავლენას ახდენს მრავალმხრივ პროგრამებზე ისე დესტრუქციულად, როგორც გლობალური ცვალებადი მდგომარეობის დაძლევის მცდელობა. რაც უფრო მეტი დეტალი უნდა მართოთ, მით უფრო სავარაუდოა, რომ თქვენი პროგრამა პიკს მიაღწევს და დაიშლება.

რეალისტურ პროგრამებში უნდა არსებობდეს გარკვეული საერთო მდგომარეობა, რომელსაც შეუძლია შეცვალოს. და ეს არის ის, სადაც პროგრამისტების უმეტესობა იწყებს პრობლემებს. პროგრამისტი ხედავს, რომ აქ საჭიროა საერთო მდგომარეობა, იქცევა მრავალძირიან არსენალში და იქიდან იღებს უმარტივეს ინსტრუმენტს: უნივერსალურ საკეტს (კრიტიკული განყოფილება, მუტექსი, ან რასაც ეძახიან). როგორც ჩანს, მათ სჯერათ, რომ ურთიერთგამორიცხვა გადაჭრის მონაცემთა გაზიარების ყველა პრობლემას.

იმ რაოდენობის პრობლემა, რომელიც შეიძლება წარმოიშვას ასეთი ერთი საკეტით, შემაძრწუნებელია. მხედველობაში უნდა იქნას მიღებული რასის პირობები, გადაჭარბებული დაბლოკვით გამოწვეული პრობლემები და განაწილების სამართლიანობის საკითხები მხოლოდ რამდენიმე მაგალითია. თუ თქვენ გაქვთ რამოდენიმე საკეტი, განსაკუთრებით თუ ისინი ჩადგმულია, მაშინ თქვენ ასევე უნდა მიიღოთ ზომები ჩიხიდან, დინამიური ჩიხიდან, რიგების ბლოკირებიდან და თანხმობასთან დაკავშირებული სხვა საფრთხეებისგან. გარდა ამისა, არსებობს თანდაყოლილი ერთჯერადი ბლოკირების პრობლემები.
როდესაც ვწერ ან განვიხილავ კოდს, მე მაქვს თითქმის უტყუარი რკინის წესი: თუ თქვენ გააკეთეთ საკეტი, მაშინ, როგორც ჩანს, სადმე შეცდომა დაუშვით.

ეს განცხადება შეიძლება გაკეთდეს ორი გზით:

  1. თუ გჭირდებათ ჩაკეტვა, მაშინ ალბათ გაქვთ გლობალური ცვალებადი მდგომარეობა, რომლის დაცვა გსურთ ერთდროულად განახლებებისგან. გლობალური ცვალებადი მდგომარეობის არსებობა ხარვეზია განაცხადის დიზაინის ფაზაში. მიმოხილვა და დიზაინი.
  2. საკეტების სწორად გამოყენება ადვილი არ არის და წარმოუდგენლად ძნელი იქნება დაბლოკვასთან დაკავშირებული შეცდომების ლოკალიზაცია. ძალიან სავარაუდოა, რომ თქვენ არასწორად გამოიყენებთ საკეტს. თუ მე ვხედავ საკეტს და პროგრამა იქცევა არაჩვეულებრივად, მაშინ პირველი რასაც ვაკეთებ არის კოდის შემოწმება, რომელიც დამოკიდებულია დაბლოკვაზე. და მე ჩვეულებრივ ვპოულობ მასში პრობლემებს.

ორივე ეს ინტერპრეტაცია სწორია.

მრავალწერტილი კოდის წერა ადვილია. მაგრამ ძალიან, ძალიან რთულია სინქრონიზაციის პრიმიტივების სწორად გამოყენება. ალბათ თქვენ არ ხართ კომპეტენტური, რომ გამოიყენოთ თუნდაც ერთი საკეტი სწორად. ყოველივე ამის შემდეგ, საკეტები და სხვა სინქრონიზაციის პრიმიტივები არის კონსტრუქციები, რომლებიც აღმართულია მთელი სისტემის დონეზე. ადამიანები, რომლებსაც შენზე გაცილებით უკეთ ესმით პარალელური პროგრამირება, იყენებენ ამ პრიმიტივებს მონაცემთა ერთდროული სტრუქტურებისა და მაღალი დონის სინქრონიზაციის კონსტრუქტების შესაქმნელად. მე და შენ, ჩვეულებრივი პროგრამისტები, უბრალოდ ვიღებთ ასეთ კონსტრუქციებს და ვიყენებთ მათ ჩვენს კოდში. პროგრამისტმა პროგრამისტმა არ უნდა გამოიყენოს დაბალი დონის სინქრონიზაციის პრიმიტივები უფრო ხშირად, ვიდრე უშუალო ზარებს მოწყობილობის დრაივერებთან. ანუ თითქმის არასოდეს.

მონაცემთა გაზიარების პრობლემების გადასაჭრელად საკეტების გამოყენების მცდელობა ჰგავს ცეცხლის ჩაქრობას თხევადი ჟანგბადით. ხანძრის მსგავსად, ასეთი პრობლემების თავიდან აცილება უფრო ადვილია, ვიდრე გამოსწორება. თუ თქვენ გათავისუფლდებით საერთო მდგომარეობიდან, თქვენ არ გჭირდებათ ბოროტად გამოყენება სინქრონიზაციის პრიმიტივებზეც.

უმეტესობა რაც თქვენ იცით მრავალსიტყვიანი ტექსტის შესახებ შეუსაბამოა

დამწყებთათვის მრავალმხრივი გაკვეთილებიდან თქვენ შეისწავლით რა არის ძაფები. შემდეგ ავტორი დაიწყებს სხვადასხვა გზების განხილვას, რომლითაც ეს ძაფები შეიძლება მუშაობდეს პარალელურად - მაგალითად, ისაუბროს გაზიარებულ მონაცემებზე წვდომის კონტროლის შესახებ საკეტებისა და სემფორების გამოყენებით, დაფიქრდეს რა შეიძლება მოხდეს მოვლენებთან მუშაობისას. ახლოდან შეისწავლის მდგომარეობის ცვლადებს, მეხსიერების ბარიერებს, კრიტიკულ მონაკვეთებს, მუტექსებს, არასტაბილურ ველებს და ატომურ ოპერაციებს. მაგალითები, თუ როგორ გამოიყენოთ ეს დაბალი დონის კონსტრუქციები ყველა სახის სისტემის ოპერაციის შესასრულებლად, განხილული იქნება. ამ მასალის ნახევრის წაკითხვის შემდეგ, პროგრამისტი გადაწყვეტს, რომ მან უკვე საკმარისად იცის ყველა ამ პრიმიტივისა და მათი გამოყენების შესახებ. ყოველივე ამის შემდეგ, თუ მე ვიცი, როგორ მუშაობს ეს სისტემა სისტემის დონეზე, შემიძლია იგივე გამოვიყენო განაცხადის დონეზე. დიახ?

წარმოიდგინეთ, უთხარით მოზარდს, თუ როგორ უნდა ააწყოს შიდა წვის ძრავა თავად. შემდეგ, მართვის ყოველგვარი ტრენინგის გარეშე, თქვენ მას აყენებთ მანქანის საჭესთან და ეუბნებით: "წადი!" მოზარდს ესმის, როგორ მუშაობს მანქანა, მაგრამ წარმოდგენა არ აქვს როგორ უნდა მივიდეს A წერტილიდან B წერტილზე.

იმის გაგება, თუ როგორ მუშაობს ძაფები სისტემის დონეზე, როგორც წესი, არანაირად არ ეხმარება პროგრამის დონეზე. მე არ ვარაუდობ, რომ პროგრამისტებს არ სჭირდებათ ამ დაბალი დონის ყველა დეტალის შესწავლა. უბრალოდ ნუ ელოდებით, რომ შეძლებთ ამ ცოდნის გამოყენებას ბიზნეს აპლიკაციის შემუშავებისას ან შემუშავებისას.

შესავალი თემატიკის ლიტერატურა (და მასთან დაკავშირებული აკადემიური კურსები) არ უნდა იკვლევდეს ასეთ დაბალი დონის კონსტრუქციებს. თქვენ უნდა გაამახვილოთ ყურადღება პრობლემების ყველაზე გავრცელებული კლასების გადაწყვეტაზე და აჩვენოთ დეველოპერებს, თუ როგორ წყდება ეს პრობლემები მაღალი დონის შესაძლებლობების გამოყენებით. პრინციპში, ბიზნეს პროგრამების უმეტესობა უკიდურესად მარტივი პროგრამებია. ისინი კითხულობენ მონაცემებს ერთი ან მეტი შეყვანის მოწყობილობიდან, ასრულებენ ამ მონაცემების კომპლექსურ დამუშავებას (მაგალითად, პროცესში, ისინი მოითხოვენ დამატებით მონაცემებს) და შემდეგ აძლევენ შედეგებს.

ხშირად, ასეთი პროგრამები მშვენივრად ჯდება მიმწოდებელ-სამომხმარებლო მოდელში, რომელიც მოითხოვს მხოლოდ სამ ძაფს:

  • შეყვანის ნაკადი კითხულობს მონაცემებს და ათავსებს მას შეყვანის რიგში;
  • მუშა ძაფი კითხულობს ჩანაწერებს შეყვანის რიგიდან, ამუშავებს მათ და ათავსებს შედეგებს გამომავალ რიგში;
  • გამომავალი ნაკადი კითხულობს ჩანაწერებს გამომავალი რიგიდან და ინახავს მათ.

ეს სამი ძაფი დამოუკიდებლად მუშაობს, მათ შორის ურთიერთობა ხდება რიგის დონეზე.

მიუხედავად იმისა, რომ ტექნიკურად ეს რიგები შეიძლება ჩაითვალოს საერთო მდგომარეობის ზონებად, პრაქტიკაში ისინი მხოლოდ საკომუნიკაციო არხებია, რომლებშიც მოქმედებს მათი შინაგანი სინქრონიზაცია. რიგები მხარს უჭერს ერთდროულად ბევრ მწარმოებელთან და მომხმარებელთან მუშაობას, თქვენ შეგიძლიათ პარალელურად დაამატოთ და წაშალოთ მათში არსებული ნივთები.

ვინაიდან შეყვანის, დამუშავების და გამომავალი ეტაპები ერთმანეთისგან იზოლირებულია, მათი განხორციელება მარტივად შეიძლება შეიცვალოს პროგრამის დანარჩენ ნაწილზე ზემოქმედების გარეშე. სანამ რიგში არსებული მონაცემების ტიპი არ იცვლება, თქვენ შეგიძლიათ შეცვალოთ პროგრამის ინდივიდუალური კომპონენტები თქვენი შეხედულებისამებრ. გარდა ამისა, ვინაიდან მომწოდებლებისა და მომხმარებლების თვითნებური რაოდენობა მონაწილეობს რიგში, არ არის რთული სხვა მწარმოებლების / მომხმარებლების დამატება. ჩვენ შეგვიძლია გვქონდეს ათეულობით შეყვანის ნაკადი, რომელიც წერს ინფორმაციას ერთსა და იმავე რიგში, ან ათობით მუშა ძაფს, რომელიც იღებს ინფორმაციას შეყვანის რიგიდან და აითვისებს მონაცემებს. ერთი კომპიუტერის ფარგლებში, ასეთი მოდელი კარგად არის მასშტაბირებული.

რაც მთავარია, თანამედროვე პროგრამირების ენები და ბიბლიოთეკები ძალიან აადვილებს პროდიუსერ-სამომხმარებლო პროგრამების შექმნას. .NET– ში ნახავთ პარალელურ კოლექციებს და TPL მონაცემთა ნაკადის ბიბლიოთეკას. ჯავას აქვს შემსრულებელი სერვისი, ასევე BlockingQueue და სხვა კლასები java.util.concurrent namespace– დან. C ++– ს აქვს Boost threading ბიბლიოთეკა და Intel– ის Thread Building Blocks ბიბლიოთეკა. Microsoft– ის Visual Studio 2013 წარუდგენს ასინქრონულ აგენტებს. მსგავსი ბიბლიოთეკები ასევე ხელმისაწვდომია Python, JavaScript, Ruby, PHP და, რამდენადაც მე ვიცი, ბევრ სხვა ენაზე. თქვენ შეგიძლიათ შექმნათ პროდიუსერ-სამომხმარებლო აპლიკაცია რომელიმე ამ პაკეტის გამოყენებით, ყოველგვარი გადაუდებლობის გარეშე საკეტების, სემფორების, მდგომარეობის ცვლადების ან სინქრონიზაციის სხვა პრიმიტივის მიმართვის გარეშე.

ამ ბიბლიოთეკებში თავისუფლად გამოიყენება სინქრონიზაციის პრიმიტივის მრავალფეროვნება. Ეს კარგია. ყველა ეს ბიბლიოთეკა დაწერილია იმ ადამიანების მიერ, რომლებსაც ესმით მრავალსიტყვიანი ტექსტი შეუდარებლად უკეთესად ვიდრე საშუალო პროგრამისტი. ასეთ ბიბლიოთეკასთან მუშაობა პრაქტიკულად იგივეა, რაც გაშვებული ენების ბიბლიოთეკა. ეს შეიძლება შევადაროთ პროგრამირებას მაღალი დონის ენაზე და არა ასამბლეის ენაზე.

მიმწოდებელი-სამომხმარებლო მოდელი მხოლოდ ერთ-ერთია მრავალი მაგალითიდან. ზემოაღნიშნული ბიბლიოთეკები შეიცავს კლასებს, რომელთა გამოყენება შესაძლებელია მრავალი საერთო დიზაინის ნიმუშის განსახორციელებლად დაბალი დონის დეტალების გარეშე. შესაძლებელია ფართომასშტაბიანი მრავალსიმიანი პროგრამების შექმნა ფიქრის გარეშე, თუ როგორ ხდება კოლხების კოორდინირება და სინქრონიზაცია.

ბიბლიოთეკებთან მუშაობა

ამრიგად, მრავალფუნქციური პროგრამების შექმნა ფუნდამენტურად არ განსხვავდება სინქრონული ერთჯერადი პროგრამების წერისგან. კაფსულაციისა და მონაცემების დამალვის მნიშვნელოვანი პრინციპები უნივერსალურია და მნიშვნელობას იძენს მხოლოდ მაშინ, როდესაც ჩართულია მრავალი ერთდროული თემა. თუ უგულებელყოფთ ამ მნიშვნელოვან ასპექტებს, მაშინ დაბალი დონის ძაფის ყველაზე სრულყოფილი ცოდნაც კი არ დაგიზოგავთ.

თანამედროვე დეველოპერებს უწევთ ბევრი პრობლემის გადაჭრა პროგრამული უზრუნველყოფის დონეზე, ხდება ისე, რომ უბრალოდ დრო არ არის ვიფიქროთ იმაზე, რაც ხდება სისტემის დონეზე. რაც უფრო რთულდება აპლიკაციები, მით უფრო რთული დეტალები უნდა იმალებოდეს API დონეებს შორის. ჩვენ ამას ვაკეთებთ ათზე მეტი წლის განმავლობაში. შეიძლება ითქვას, რომ პროგრამისტისგან სისტემის სირთულის თვისობრივი დამალვა არის მთავარი მიზეზი, რის გამოც პროგრამისტს შეუძლია დაწეროს თანამედროვე პროგრამები. ამასთან დაკავშირებით, ჩვენ არ ვმალავთ სისტემის სირთულეს UI შეტყობინებების მარყუჟის განხორციელებით, დაბალი დონის საკომუნიკაციო პროტოკოლების შექმნით და ა.შ.?

ანალოგიური სიტუაციაა მრავალწერტილთან დაკავშირებით. მრავალმხრივი სცენარების უმრავლესობა, რომელსაც საშუალო ბიზნეს პროგრამისტი შეიძლება შეხვდეს, უკვე კარგად არის ცნობილი და კარგად არის დანერგილი ბიბლიოთეკებში. ბიბლიოთეკის ფუნქციები დიდ საქმეს მალავს პარალელიზმის უზარმაზარ სირთულეს. თქვენ უნდა ისწავლოთ როგორ გამოიყენოთ ეს ბიბლიოთეკები ისევე, როგორც თქვენ იყენებთ ინტერფეისის ელემენტების ბიბლიოთეკებს, საკომუნიკაციო პროტოკოლებს და უამრავ სხვა ინსტრუმენტს, რომლებიც უბრალოდ მუშაობს. დაბალი დონის მრავალსიტყვაობა დაუტოვეთ სპეციალისტებს - ბიბლიოთეკების ავტორებს, რომლებიც გამოიყენება პროგრამების შესაქმნელად.

NS ეს სტატია არ არის გამოცდილი პითონის მომთვინიერებლებისთვის, ვისთვისაც გველის ამ ბურთის ამოხსნა ბავშვის თამაშია, არამედ ახლადდამოკიდებული პითონის მრავალმხრივი შესაძლებლობების ზედაპირული მიმოხილვა.

სამწუხაროდ, რუსულ ენაზე არ არის იმდენი მასალა პითონში მრავალსიტყვიანი თემის შესახებ, და პითონერებმა, რომელთაც არაფერი სმენიათ, მაგალითად, GIL– ის შესახებ, შესაშური რეგულარობით დაიწყეს ჩემთან შეხვედრა. ამ სტატიაში შევეცდები აღვწერო მრავალფუნქციური პითონის ყველაზე ძირითადი მახასიათებლები, გითხრათ რა არის GIL და როგორ უნდა იცხოვროთ მასთან (ან მის გარეშე) და მრავალი სხვა.


პითონი არის მომხიბლავი პროგრამირების ენა. ის შესანიშნავად აერთიანებს პროგრამირების მრავალ პარადიგმას. იმ ამოცანების უმეტესობა, რომელსაც პროგრამისტი შეხვდება, აქ მარტივად, ელეგანტურად და ლაკონურად წყდება. მაგრამ ყველა ამ პრობლემისთვის, ერთი ხრახნიანი გადაწყვეტა ხშირად საკმარისია, ხოლო ერთი ხრახნიანი პროგრამები, როგორც წესი, პროგნოზირებადია და ადვილად გამოსწორებადია. იგივეს თქმა არ შეიძლება მრავალპროფილიანი და მრავალპროფილიანი პროგრამების შესახებ.

მრავალჯერადი ხრახნიანი პროგრამები


პითონს აქვს მოდულიხრახნიანი , და მას აქვს ყველაფერი რაც თქვენ გჭირდებათ მრავალწახნაგოვანი პროგრამირებისათვის: არსებობს სხვადასხვა სახის საკეტები და სემაფორი და ღონისძიების მექანიზმი. ერთი სიტყვით - ყველაფერი, რაც საჭიროა მრავალმხრივი პროგრამების უმრავლესობისთვის. უფრო მეტიც, ყველა ამ ინსტრუმენტის გამოყენება საკმაოდ მარტივია. განვიხილოთ პროგრამის მაგალითი, რომელიც იწყებს 2 ძაფს. ერთი თემა წერს ათს "0", მეორე - ათი "1" დამკაცრად თავის მხრივ.

ძაფის შემოტანა

დეფ მწერალი

მე i xrange (10):

ბეჭდვა x

Event_for_set.set ()

# ინტიმური მოვლენა

e1 = ძაფი. მოვლენა ()

e2 = ძაფი. მოვლენა ()

# init თემა

0, e1, e2))

1, e2, e1))

# თემების დაწყება

t1. დაწყება ()

t2. დაწყება ()

t1. შეერთება ()

t2. შეერთება ()


არანაირი ჯადოსნური ან ვუდუს კოდი. კოდი ნათელია და თანმიმდევრული. უფრო მეტიც, როგორც ხედავთ, ჩვენ შევქმენით ნაკადი ფუნქციისგან. ეს ძალიან მოსახერხებელია მცირე დავალებებისთვის. ეს კოდი ასევე საკმაოდ მოქნილია. დავუშვათ, რომ ჩვენ გვაქვს მესამე პროცესი, რომელიც წერს "2", მაშინ კოდი ასე გამოიყურება:

ძაფის შემოტანა

დეფ მწერალი (x, მოვლენა_ ლოდინისთვის, მოვლენა_დაწყებისათვის):

მე i xrange (10):

Event_for_wait.wait () # ლოდინი ღონისძიებაზე

Event_for_wait.clear () # სუფთა ღონისძიება მომავლისთვის

ბეჭდვა x

Event_for_set.set () # დააყენეთ ღონისძიება მეზობელი ძაფისთვის

# ინტიმური მოვლენა

e1 = ძაფი. მოვლენა ()

e2 = ძაფი. მოვლენა ()

e3 = ძაფი. მოვლენა ()

# init თემა

t1 = ძაფი. თემა (სამიზნე = მწერალი, არგსი = ( 0, e1, e2))

t2 = თემა. თემა (სამიზნე = მწერალი, არგსი = ( 1, e2, e3))

t3 = ძაფი. თემა (სამიზნე = მწერალი, არგსი = ( 2, e3, e1))

# თემების დაწყება

t1. დაწყება ()

t2. დაწყება ()

t3. დაწყება ()

e1.set () # ინიცირება პირველი ღონისძიება

# შეუერთეთ ძაფები მთავარ თემას

t1. შეერთება ()

t2. შეერთება ()

t3. შეერთება ()


ჩვენ დავამატეთ ახალი მოვლენა, ახალი თემა და ოდნავ შევცვალეთ ის პარამეტრები
ნაკადი იწყება (რა თქმა უნდა, შეგიძლიათ დაწეროთ უფრო ზოგადი გადაწყვეტა, მაგალითად, MapReduce, მაგრამ ეს სცილდება ამ სტატიის ფარგლებს).
როგორც ხედავთ, ჯადოქრობა ჯერ კიდევ არ არსებობს. ყველაფერი არის მარტივი და პირდაპირი. წავიდეთ უფრო შორს.

გლობალური თარჯიმნის საკეტი


ძაფების გამოყენების ორი ყველაზე გავრცელებული მიზეზი არსებობს: პირველი, თანამედროვე პროცესორების მრავალპროფილიანი არქიტექტურის გამოყენების ეფექტურობის გაზრდა და, შესაბამისად, პროგრამის შესრულება;
მეორეც, თუ ჩვენ გვჭირდება პროგრამის ლოგიკის დაყოფა პარალელურ, სრულად ან ნაწილობრივ ასინქრონულ ნაწილებად (მაგალითად, რომ შევძლოთ რამდენიმე სერვერის ერთდროულად დალევა).

პირველ შემთხვევაში, ჩვენ ვხვდებით პითონის (უფრო სწორად მისი ძირითადი CPython განხორციელების) შეზღუდვას, როგორიცაა გლობალური თარჯიმნის საკეტი (ან შემოკლებით GIL). GIL– ის კონცეფცია იმაში მდგომარეობს, რომ პროცესორის მიერ ერთდროულად შეიძლება შესრულდეს მხოლოდ ერთი ძაფი. ეს კეთდება ისე, რომ არ მოხდეს ძაფებს შორის ბრძოლა ცალკეულ ცვლადებზე. შესრულებადი თემა იძენს წვდომას მთელ გარემოზე. პითონში ძაფის დანერგვის ეს მახასიათებელი მნიშვნელოვნად ამარტივებს ძაფებთან მუშაობას და იძლევა ძაფის გარკვეულ უსაფრთხოებას.

მაგრამ არის ერთი დახვეწილი წერტილი: შეიძლება ჩანდეს, რომ მრავალხაზოვანი აპლიკაცია იმუშავებს ზუსტად იმდენ დროს, რამდენიც ერთი ხრახნიანი აპლიკაცია იმავეს აკეთებს, ან CPU- ზე თითოეული ძაფის შესრულების დროის ჯამი. მაგრამ აქ ერთი უსიამოვნო ეფექტი გველოდება. განიხილეთ პროგრამა:

ღია ("test1.txt", "w") როგორც fout:

მე xrange- ში (1000000):

ამობეჭდვა >> fout, 1


ეს პროგრამა წერს მილიონ სტრიქონს "1" ფაილზე და ამას აკეთებს ~ 0.35 წამში ჩემს კომპიუტერში.

განვიხილოთ სხვა პროგრამა:

საწყისი threading იმპორტი თემა

def მწერალი (ფაილის სახელი, n):

ღია (ფაილის სახელი, "w") როგორც fout:

მე xrange- ში (n):

ამობეჭდვა >> fout, 1

t1 = თემა (სამიზნე = მწერალი, args = ("test2.txt", 500000,))

t2 = თემა (სამიზნე = მწერალი, args = ("test3.txt", 500000,))

t1. დაწყება ()

t2. დაწყება ()

t1. შეერთება ()

t2. შეერთება ()


ეს პროგრამა ქმნის 2 ძაფს. თითოეულ თემაში ის წერს ცალკე ფაილს, ნახევარი მილიონი სტრიქონი "1". სინამდვილეში, სამუშაოს მოცულობა იგივეა, რაც წინა პროგრამაში. დროთა განმავლობაში, აქ მიიღება საინტერესო ეფექტი. პროგრამას შეუძლია გაუშვას 0.7 წამიდან 7 წამამდე. Რატომ ხდება ეს?

ეს გამოწვეულია იმით, რომ როდესაც ძაფს არ სჭირდება პროცესორის რესურსი, ის ავრცელებს GIL– ს და ამ მომენტში მას შეუძლია სცადოს მისი მოპოვება, და სხვა ძაფი, და ასევე მთავარი ძაფი. ამავდროულად, ოპერაციულ სისტემას, რომელმაც იცის, რომ ბევრი ბირთვია, შეუძლია გაამწვავოს ყველაფერი ბირთვებს შორის ძაფების გავრცელების მცდელობით.

UPD: ამ დროისთვის, Python 3.2– ში არის GIL– ის გაუმჯობესებული განხორციელება, რომელშიც ეს პრობლემა ნაწილობრივ მოგვარებულია, კერძოდ, იმის გამო, რომ თითოეული ძაფი, კონტროლის დაკარგვის შემდეგ, ელოდება მოკლე პერიოდს მანამდე შეუძლია კვლავ დაიჭიროს GIL (ინგლისურ ენაზე კარგი პრეზენტაციაა)

”ასე რომ თქვენ არ შეგიძლიათ დაწეროთ ეფექტური მრავალსიდიანი პროგრამები პითონში?” თქვენ ჰკითხავთ. არა, რა თქმა უნდა, არსებობს გამოსავალი და კიდევ რამდენიმე.

პროგრამების მრავალპროფილიანი დამუშავება


წინა პარაგრაფში აღწერილი პრობლემის გარკვეულწილად გადაჭრის მიზნით, პითონს აქვს მოდულიქვეპროცესები ... ჩვენ შეგვიძლია დავწეროთ პროგრამა, რომლის შესრულებაც ჩვენ გვსურს პარალელურ თემაში (ფაქტობრივად, უკვე პროცესი). და გაუშვით ერთი ან მეტი თემა სხვა პროგრამაში. ეს ნამდვილად დააჩქარებს ჩვენს პროგრამას, რადგან GIL გამშვებ სისტემაში შექმნილი ძაფები არ იშლება, არამედ მხოლოდ დაელოდება გაშვებული პროცესის დასრულებას. თუმცა, ამ მეთოდს ბევრი პრობლემა აქვს. მთავარი პრობლემა ის არის, რომ ძნელია მონაცემთა გადაცემა პროცესებს შორის. თქვენ მოგიწევთ როგორმე მოახდინოთ ობიექტების სერიალიზაცია, კომუნიკაციის დამყარება PIPE ან სხვა ინსტრუმენტების საშუალებით, მაგრამ ეს ყველაფერი გარდაუვალია და კოდის გაგება რთულდება.

სხვა მიდგომა დაგვეხმარება აქ. პითონს აქვს მრავალპროფილიანი მოდული ... ფუნქციონალური თვალსაზრისით, ეს მოდული ჰგავსხრახნიანი ... მაგალითად, პროცესები ერთნაირად შეიძლება შეიქმნას რეგულარული ფუნქციებისგან. პროცესებთან მუშაობის მეთოდები თითქმის იგივეა, რაც ძაფების მოდულიდან ძაფებისთვის. მაგრამ პროცესების სინქრონიზაციისათვის და მონაცემთა გაცვლისთვის ჩვეულებრივია სხვა ინსტრუმენტების გამოყენება. ჩვენ ვსაუბრობთ რიგებზე (რიგი) და მილები (მილები). ამასთან, აქ არის დაბლოკვის, მოვლენებისა და სემფორების ანალოგიები.

გარდა ამისა, მრავალპროცესირების მოდულს აქვს საერთო მეხსიერებასთან მუშაობის მექანიზმი. ამისათვის, მოდულს აქვს ცვლადის (ღირებულების) და მასივის (მასივის) კლასები, რომლებიც შეიძლება „გაზიარდეს“ პროცესებს შორის. საერთო ცვლადებთან მუშაობის მოხერხებულობისთვის შეგიძლიათ გამოიყენოთ მენეჯერის კლასები. ისინი უფრო მოქნილი და ადვილი გამოსაყენებელია, მაგრამ ნელა. უნდა აღინიშნოს, რომ არსებობს კარგი შესაძლებლობა, რომ მოხდეს საერთო ტიპები ctypes მოდულიდან multiprocessing.sharedctypes მოდულის გამოყენებით.

ასევე მრავალპროცესირების მოდულში არის მექანიზმი პროცესის აუზების შესაქმნელად. ეს მექანიზმი ძალიან მოსახერხებელია გამოსაყენებლად Master-Worker ნიმუშის განსახორციელებლად ან პარალელური რუქის განსახორციელებლად (რაც გარკვეულწილად არის ოსტატ-მუშაკის განსაკუთრებული შემთხვევა).

მრავალპროფილიანი მოდულთან მუშაობის ძირითადი პრობლემებიდან აღსანიშნავია ამ მოდულის ფარდობითი პლატფორმის დამოკიდებულება. ვინაიდან პროცესებთან მუშაობა განსხვავებულად არის ორგანიზებული სხვადასხვა ოპერაციულ სისტემაში, გარკვეული შეზღუდვები დაწესებულია კოდზე. მაგალითად, Windows– ს არ აქვს ჩანგლის მექანიზმი, ამიტომ პროცესის გამოყოფის წერტილი უნდა იყოს გახვეული:

თუ __name__ == "__ მთავარი__":


თუმცა, ეს დიზაინი უკვე კარგი ფორმაა.

Სხვა რა...


არსებობს სხვა ბიბლიოთეკები და მიდგომები პითონში პარალელური პროგრამების დასაწერად. მაგალითად, შეგიძლიათ გამოიყენოთ Hadoop + Python ან სხვადასხვა Python MPI განხორციელება (pyMPI, mpi4py). თქვენ შეგიძლიათ გამოიყენოთ არსებული C ++ ან Fortran ბიბლიოთეკების შეფუთვაც კი. აქ შეიძლება აღინიშნოს ისეთი ჩარჩოები / ბიბლიოთეკები, როგორიცაა Pyro, Twisted, Tornado და მრავალი სხვა. მაგრამ ეს ყველაფერი უკვე სცილდება ამ სტატიის ფარგლებს.

თუ მოგეწონათ ჩემი სტილი, შემდეგ სტატიაში შევეცდები გითხრათ როგორ დავწეროთ მარტივი თარჯიმნები PLY– ში და რისთვის შეიძლება მათი გამოყენება.

თავი 10

მრავალჯერადი ხრახნიანი პროგრამები

თანამედროვე ოპერაციულ სისტემებში მრავალმხრივი ამოცანა მიღებულია [ Apple OS X– ის მოსვლამდე, Macintosh კომპიუტერებზე არ არსებობდა თანამედროვე მრავალფუნქციური ოპერაციული სისტემა. ძალიან რთულია ოპერაციული სისტემის სწორად შემუშავება სრული მრავალფუნქციური ამოცანებით, ამიტომ OS X უნდა ეფუძნებოდეს Unix- ს.]. მომხმარებელი ელოდება, რომ როდესაც ტექსტური რედაქტორი და ფოსტის კლიენტი ერთდროულად ამოქმედდება, ეს პროგრამები არ იქნება კონფლიქტური და ელ.ფოსტის მიღებისას რედაქტორი არ შეწყვეტს მუშაობას. როდესაც რამდენიმე პროგრამა ერთდროულად იწყებს მუშაობას, ოპერაციული სისტემა სწრაფად გადადის პროგრამებს შორის, რითაც თავის მხრივ აწვდის მათ პროცესორს (თუ, რა თქმა უნდა, კომპიუტერზე რამდენიმე პროცესორი არ არის დაინსტალირებული). Როგორც შედეგი, ილუზიარამდენიმე პროგრამის ერთდროულად გაშვება, რადგანაც საუკეთესო საბეჭდი მანქანა (და უსწრაფესი ინტერნეტ კავშირი) კი ვერ ახერხებს შეცვალოს თანამედროვე პროცესორი.

მულტიტრედი, გარკვეული გაგებით, შეიძლება ჩაითვალოს მულტიტასკინგის მომდევნო დონეზე: ნაცვლად იმისა, რომ გადავიდეთ სხვადასხვა პროგრამები,ოპერაციული სისტემა გადადის ერთი და იმავე პროგრამის სხვადასხვა ნაწილს შორის. მაგალითად, მრავალფუნქციური ელ.ფოსტის კლიენტი საშუალებას გაძლევთ მიიღოთ ახალი ელ.ფოსტის შეტყობინებები კითხვისას ან ახალი შეტყობინებების შედგენისას. დღესდღეობით, მულტიტრედი ასევე ბევრ მომხმარებელს მიაჩნია.

VB– ს არასოდეს ჰქონია ნორმალური მრავალსიდიანი მხარდაჭერა. მართალია, მისი ერთ -ერთი სახეობა გამოჩნდა VB5– ში - ნაკადის ერთობლივი მოდელი(ბინის ძაფები). როგორც მალე დაინახავთ, ერთობლივი მოდელი პროგრამისტს აწვდის მულტიტრედის ზოგიერთ სარგებელს, მაგრამ ის სრულად არ იყენებს ყველა მახასიათებელს. ადრე თუ გვიან, თქვენ უნდა გადახვიდეთ სასწავლო აპარატზე რეალურზე და VB .NET გახდა VB– ს პირველი ვერსია უფასო მრავალმხრივი მოდელის მხარდაჭერით.

თუმცა, მრავალსიდიანი არ არის ერთ -ერთი მახასიათებელი, რომელიც ადვილად გამოიყენება პროგრამირების ენებზე და ადვილად აითვისა პროგრამისტებმა. რატომ?

იმის გამო, რომ მრავალხარისხოვან აპლიკაციებში შეიძლება წარმოიშვას ძალიან სახიფათო შეცდომები, რომლებიც არაპროგნოზირებად ჩნდება და ქრება (და ასეთი შეცდომების გამოსწორება ყველაზე რთულია).

გულწრფელად გაფრთხილება: მულტიტრედი არის პროგრამირების ერთ -ერთი ყველაზე რთული სფერო. უმცირესი უყურადღებობა იწვევს გაურკვეველი შეცდომების გაჩენას, რომელთა გამოსწორებას ასტრონომიული თანხები სჭირდება. ამ მიზეზით, ეს თავი შეიცავს ბევრს ცუდიმაგალითები - ჩვენ შეგნებულად დავწერეთ ისინი ისე, რომ დავანახოთ საერთო შეცდომები. ეს არის ყველაზე ხერხემლიანი პროგრამირების სწავლის ყველაზე უსაფრთხო მიდგომა: თქვენ უნდა შეგეძლოთ გამოავლინოთ პოტენციური პრობლემები, როდესაც ერთი შეხედვით ყველაფერი კარგად მუშაობს და იცოდეთ როგორ გადაჭრათ ისინი. თუ გსურთ გამოიყენოთ მრავალფუნქციური ხრახნიანი პროგრამირების ტექნიკა, ამის გარეშე არ შეგიძლიათ.

ეს თავი მყარ საფუძველს ჩაუყრის შემდგომ დამოუკიდებელ მუშაობას, მაგრამ ჩვენ ვერ შევძლებთ აღწეროთ მრავალსართულიანი პროგრამირება ყველა სირთულეში - მხოლოდ დაბეჭდილი დოკუმენტაცია Threading სახელების სივრცის კლასებზე იღებს 100 -ზე მეტ გვერდს. თუ გსურთ მრავალმხრივი პროგრამირების დაუფლება უფრო მაღალ დონეზე, მიმართეთ სპეციალიზებულ წიგნებს.

რაც არ უნდა საშიში იყოს მრავალსიდიანი პროგრამირება, ის აუცილებელია ზოგიერთი პრობლემის პროფესიონალური გადაწყვეტისათვის. თუ თქვენი პროგრამები საჭიროებისამებრ არ იყენებენ მრავალსიტყვაობას, მომხმარებლები იმედგაცრუებულნი დარჩებიან და სხვა პროდუქტს ამჯობინებენ. მაგალითად, პოპულარული ელექტრონული ფოსტის პროგრამის Eudora მხოლოდ მეოთხე ვერსიაში გამოჩნდა მრავალსართულიანი შესაძლებლობები, რომელთა გარეშე შეუძლებელია წარმოვიდგინოთ რაიმე თანამედროვე პროგრამა ელექტრონულ ფოსტაზე მუშაობისთვის. იმ დროისთვის, როდესაც ეუდორამ შემოიღო მრავალსიდიანი მხარდაჭერა, ბევრი მომხმარებელი (მათ შორის ამ წიგნის ერთ -ერთი ავტორი) გადავიდა სხვა პროდუქტზე.

დაბოლოს, .NET– ში, ერთი ხრახნიანი პროგრამები უბრალოდ არ არსებობს. ყველაფერი.NET პროგრამები მრავალმხრივია, რადგან ნაგვის შემგროვებელი მუშაობს როგორც დაბალი პრიორიტეტული ფონის პროცესი. როგორც ნაჩვენებია ქვემოთ.

გაცნობა მრავალსიდიანი

თითოეული პროგრამა მუშაობს კონკრეტულად კონტექსტი,აღწერს კოდისა და მონაცემების გავრცელებას მეხსიერებაში. კონტექსტის დაზოგვით, ფაქტობრივად ინახება პროგრამის ნაკადის მდგომარეობა, რაც საშუალებას გაძლევთ აღადგინოთ იგი მომავალში და გააგრძელოთ პროგრამის შესრულება.

კონტექსტის დაზოგვას თან ახლავს დრო და მეხსიერება. ოპერაციულ სისტემას ახსოვს პროგრამის ძაფის მდგომარეობა და გადასცემს კონტროლს სხვა ძაფზე. როდესაც პროგრამას სურს გააგრძელოს შეჩერებული ძაფის შესრულება, შენახული კონტექსტი უნდა აღდგეს, რასაც კიდევ უფრო მეტი დრო სჭირდება. ამრიგად, მულტიტრედი უნდა იქნას გამოყენებული მხოლოდ მაშინ, როდესაც სარგებელი ანაზღაურებს ყველა ხარჯს. ზოგიერთი ტიპიური მაგალითი ჩამოთვლილია ქვემოთ.

  • პროგრამის ფუნქციონირება აშკარად და ბუნებრივად იყოფა რამდენიმე არაერთგვაროვან ოპერაციად, როგორც მაგალითად ელ.ფოსტის მიღების და ახალი შეტყობინებების მომზადებასთან დაკავშირებით.
  • პროგრამა ასრულებს ხანგრძლივ და რთულ გამოთვლებს და თქვენ არ გსურთ გრაფიკული ინტერფეისი დაბლოკილი იყოს გამოთვლების ხანგრძლივობის განმავლობაში.
  • პროგრამა მუშაობს მულტიპროცესორულ კომპიუტერზე ოპერაციული სისტემით, რომელიც მხარს უჭერს მრავალი პროცესორის გამოყენებას (სანამ აქტიური ძაფების რაოდენობა არ აღემატება პროცესორების რაოდენობას, პარალელური შესრულება პრაქტიკულად თავისუფალია ძაფების გადართვასთან დაკავშირებული ხარჯებისგან).

სანამ მრავალმხრივი პროგრამების მექანიკაზე გადავალთ, აუცილებელია აღვნიშნო ერთი გარემოება, რომელიც ხშირად იწვევს დაბნეულობას დამწყებთათვის მრავალძირიანი პროგრამირების სფეროში.

პროგრამის ნაკადში ხორციელდება პროცედურა და არა ობიექტი.

ძნელი სათქმელია, რას გულისხმობს გამოთქმა "ობიექტი ასრულებს", მაგრამ ერთ -ერთი ავტორი ხშირად ასწავლის სემინარებს მრავალსიდიანი პროგრამირების შესახებ და ეს კითხვა სხვებზე უფრო ხშირად ისმება. ალბათ ვინმე ფიქრობს, რომ პროგრამის ძაფის მუშაობა იწყება კლასის ახალ მეთოდზე გამოძახებით, რის შემდეგაც ძაფი ამუშავებს ყველა შეტყობინებას, რომელიც გადაეცემა შესაბამის ობიექტს. ასეთი წარმოდგენები აბსოლუტურადცდებიან ერთი ობიექტი შეიძლება შეიცავდეს რამდენიმე ძაფს, რომლებიც ასრულებენ სხვადასხვა (და ზოგჯერ ერთსა და იმავე) მეთოდებს, ხოლო ობიექტის შეტყობინებები გადაცემულია და მიიღება რამდენიმე სხვადასხვა ძაფით (სხვათა შორის, ეს არის ერთ-ერთი მიზეზი, რაც ართულებს მრავალძრავიან პროგრამირებას: პროგრამის გამართვის მიზნით, თქვენ უნდა გაარკვიოთ რომელი ძაფი მოცემულ მომენტში ასრულებს ამა თუ იმ პროცედურას!).

ვინაიდან ძაფები იქმნება ობიექტების მეთოდებისგან, თავად ობიექტი ჩვეულებრივ იქმნება ძაფის წინ. ობიექტის წარმატებით შექმნის შემდეგ, პროგრამა ქმნის ძაფს, გადასცემს მას ობიექტის მეთოდის მისამართს და მხოლოდ ამის შემდეგაძლევს ბრძანებას დაიწყოს ძაფის შესრულება. პროცედურას, რომლისთვისაც შეიქმნა ძაფი, ისევე როგორც ყველა პროცედურას, შეუძლია შექმნას ახალი ობიექტები, შეასრულოს ოპერაციები არსებულ ობიექტებზე და გამოიძახოს სხვა პროცედურები და ფუნქციები, რომლებიც მის სფეროშია.

კლასების საერთო მეთოდები ასევე შეიძლება შესრულდეს პროგრამის ძაფებში. ამ შემთხვევაში, ასევე გახსოვდეთ კიდევ ერთი მნიშვნელოვანი გარემოება: ძაფი მთავრდება იმ პროცედურიდან გასვლით, რისთვისაც იგი შეიქმნა. პროგრამის ნაკადის ნორმალური დასრულება შეუძლებელია პროცედურის დასრულებამდე.

ძაფები შეიძლება შეწყდეს არა მხოლოდ ბუნებრივად, არამედ არანორმალურადაც. ეს ზოგადად არ არის რეკომენდებული. დამატებითი ინფორმაციისთვის იხილეთ ნაკადების შეწყვეტა და შეწყვეტა.

ძირითადი .NET ინსტრუმენტები, რომლებიც დაკავშირებულია პროგრამის ძაფების გამოყენებასთან, კონცენტრირებულია Threading სახელების სივრცეში. ამიტომ, მრავალმხრივი პროგრამების უმეტესობა უნდა დაიწყოს შემდეგი ხაზით:

იმპორტის სისტემა. თემა

სახელების სივრცის იმპორტი აადვილებს თქვენს პროგრამას აკრეფას და შესაძლებელს გახდის IntelliSense ტექნოლოგიას.

ნაკადების პირდაპირი კავშირი პროცედურებთან ვარაუდობს, რომ ამ სურათზე, დელეგატები(იხ. თავი 6). კერძოდ, Threading სახელების სივრცე მოიცავს ThreadStart დელეგატს, რომელიც ჩვეულებრივ გამოიყენება პროგრამის ძაფების დაწყებისას. ამ დელეგატის გამოყენების სინტაქსი ასე გამოიყურება:

საჯარო დელეგატი Sub ThreadStart ()

ThreadStart დელეგატთან გამოძახებულ კოდს არ უნდა ჰქონდეს პარამეტრები ან დასაბრუნებელი მნიშვნელობა, ამიტომ ძაფები არ შეიძლება შეიქმნას ფუნქციებისთვის (რომლებიც დააბრუნებენ მნიშვნელობას) და პარამეტრების მქონე პროცედურებისთვის. ნაკადიდან ინფორმაციის გადასატანად, თქვენ ასევე უნდა მოძებნოთ ალტერნატიული საშუალებები, რადგან შესრულებული მეთოდები არ აბრუნებს მნიშვნელობებს და ვერ გამოიყენებს გადაცემას მითითებით. მაგალითად, თუ ThreadMethod არის WilluseThread კლასში, მაშინ ThreadMethod– ს შეუძლია ინფორმაციის გადაცემა WillUseThread კლასის მაგალითების თვისებების შეცვლით.

განაცხადის დომენები

.NET ძაფები გაშვებულია ეგრეთ წოდებულ პროგრამის დომენებში, განსაზღვრულია დოკუმენტაციაში როგორც "ქვიშის ყუთი, რომელშიც პროგრამა მუშაობს". პროგრამის დომენი შეიძლება ჩაითვალოს, როგორც Win32 პროცესების მსუბუქი ვერსია; ერთი Win32 პროცესი შეიძლება შეიცავდეს რამოდენიმე პროგრამის დომენს. პროგრამის დომენებსა და პროცესებს შორის მთავარი განსხვავება ისაა, რომ Win32 პროცესს აქვს საკუთარი მისამართების სივრცე (დოკუმენტაციაში განაცხადის დომენები ასევე შედარებულია ფიზიკურ პროცესში მიმდინარე ლოგიკურ პროცესებთან). NET– ში, მეხსიერების ყველა მენეჯმენტს ახორციელებს გაშვების დრო, ამიტომ მრავალჯერადი პროგრამის დომენი შეიძლება გაშვებული იყოს ერთ Win32 პროცესში. ამ სქემის ერთ -ერთი სარგებელი არის პროგრამების სკალირების გაუმჯობესებული შესაძლებლობები. პროგრამის დომენებთან მუშაობის ინსტრუმენტები არის AppDomain კლასში. ჩვენ გირჩევთ შეისწავლოთ ამ კლასის დოკუმენტაცია. მისი დახმარებით თქვენ შეგიძლიათ მიიღოთ ინფორმაცია იმ გარემოს შესახებ, რომელშიც თქვენი პროგრამა მუშაობს. კერძოდ, AppDomain კლასი გამოიყენება .NET სისტემის კლასებზე ასახვისას. შემდეგ პროგრამაში მოცემულია დატვირთული შეკრებები.

იმპორტის სისტემა. ასახვა

მოდულის მოდული

ქვე ძირითადი ()

Dim theDomain როგორც AppDomain

theDomain = AppDomain.CurrentDomain

Dim ასამბლეები () As

ასამბლეები = theDomain.GetAssemblies

Dim anAssemblyxAs

თითოეული ასამბლეის ასამბლეებში

Console.WriteLinetanAssembly.Full Name) შემდეგი

Console.ReadLine ()

დასასრული ქვე

დასრულების მოდული

ნაკადების შექმნა

დავიწყოთ რუდიმენტალური მაგალითით. ვთქვათ, თქვენ გინდათ განახორციელოთ პროცედურა ცალკეულ თემაში, რომელიც ამცირებს მრიცხველს უსასრულო მარყუჟში. პროცედურა განისაზღვრება, როგორც კლასის ნაწილი:

საჯარო კლასი WillUseThreads

Public Sub SubtractFromCounter ()

Dim ითვლება როგორც მთელი რიცხვი

Do while True ითვლიან - = 1

Console.WriteLlne ("სხვა თემაში ვარ და მრიცხველი ="

& დათვლა)

მარყუჟი

დასასრული ქვე

დასასრულის კლასი

ვინაიდან Do მარყუჟის მდგომარეობა ყოველთვის მართალია, თქვენ შეიძლება იფიქროთ, რომ არაფერი ხელს არ შეუშლის SubtractFromCounter პროცედურას. თუმცა, მრავალსართულიან პროგრამაში ეს ყოველთვის ასე არ არის.

შემდეგი ნაწყვეტი აჩვენებს Sub Main პროცედურას, რომელიც იწყებს ძაფს და იმპორტის ბრძანებას:

ვარიანტი მკაცრი იმპორტის სისტემაზე. თემა მოდულის მოდული

ქვე ძირითადი ()

1 Dim myTest As New WillUseThreads ()

2 Dim bThreadStart როგორც ახალი ThreadStart (AddressOf _

myTest.SubtractFromCounter)

3 Dim bThread როგორც ახალი თემა (bThreadStart)

4 "bThread.Start ()

Dim i როგორც მთელი რიცხვი

5 გააკეთე სანამ მართალია

Console.WriteLine ("მთავარ თემაში და ითვლიან" & i) i + = 1

მარყუჟი

დასასრული ქვე

დასრულების მოდული

მოდით შევხედოთ ყველაზე მნიშვნელოვან პუნქტებს თანმიმდევრობით. უპირველეს ყოვლისა, Sub Man n პროცედურა ყოველთვის მუშაობს მთავარი ნაკადი(მთავარი თემა). .NET პროგრამებში ყოველთვის არის მინიმუმ ორი თემა გაშვებული: მთავარი ძაფი და ნაგვის შეგროვების ძაფი. ხაზი 1 ქმნის ტესტის კლასის ახალ მაგალითს. მე -2 სტრიქონში, ჩვენ ვქმნით ThreadStart დელეგატს და ვაძლევთ SubtractFromCounter პროცედურის მისამართს 1 სტრიქონში შექმნილ სატესტო კლასის მაგალითზე (ამ პროცედურას უწოდებენ პარამეტრების გარეშე). კარგიThreading სახელების სივრცის იმპორტით, გრძელი სახელის გამოტოვება შესაძლებელია. ახალი ძაფის ობიექტი იქმნება ხაზზე 3. ყურადღება მიაქციეთ ThreadStart დელეგატის გავლას Thread კლასის კონსტრუქტორის გამოძახებისას. ზოგიერთი პროგრამისტი ამჯობინებს ამ ორი სტრიქონის გაერთიანებას ერთ ლოგიკურ ხაზად:

Dim bThread როგორც ახალი თემა (New ThreadStarttAddressOf _

myTest.SubtractFromCounter))

დაბოლოს, სტრიქონი 4 "იწყებს" ძაფს ThreadStart დელეგატისთვის შექმნილი Thread ინსტანციის Start მეთოდის გამოძახებით. ამ მეთოდის გამოძახებით, ჩვენ ვეუბნებით ოპერაციულ სისტემას, რომ გამოკლების პროცედურა ცალკე თემაში უნდა მუშაობდეს.

სიტყვა "იწყება" წინა პარაგრაფში ჩასმულია ბრჭყალებში, რადგან ეს არის მრავალსართულიანი პროგრამირების მრავალი უცნაურობიდან: დაწყების დარეკვა ფაქტობრივად არ იწყებს ძაფს! ის მხოლოდ ოპერატიულ სისტემას ეუბნება, რომ დანიშნოს განსაზღვრული თემა, მაგრამ პროგრამის კონტროლს არ ექვემდებარება პირდაპირ დაწყება. თქვენ ვერ შეძლებთ ძაფების შესრულებას დამოუკიდებლად, რადგან ოპერაციული სისტემა ყოველთვის აკონტროლებს ძაფების შესრულებას. მოგვიანებით განყოფილებაში თქვენ შეისწავლით თუ როგორ გამოიყენოთ პრიორიტეტი, რათა ოპერაციული სისტემა უფრო სწრაფად დაიწყოს თქვენი თემა.

ლეღვი 10.1 გვიჩვენებს მაგალითს იმისა, თუ რა შეიძლება მოხდეს პროგრამის დაწყების შემდეგ და შემდეგ მისი შეწყვეტის Ctrl + შესვენების ღილაკით. ჩვენს შემთხვევაში, ახალი ძაფი დაიწყო მხოლოდ მას შემდეგ, რაც მთავარ ძაფში მრიცხველი გაიზარდა 341 -მდე!

ბრინჯი 10.1. მარტივი მრავალფუნქციური ხრახნიანი პროგრამული უზრუნველყოფის გაშვების დრო

თუ პროგრამა მუშაობს უფრო დიდი ხნის განმავლობაში, შედეგი იქნება დაახლოებით იგივე, რაც ნაჩვენებია ნახატზე. 10.2. ჩვენ ვხედავთ, რომ თქვენგაშვებული ძაფის დასრულება შეჩერებულია და კონტროლი კვლავ გადადის მთავარ ძაფზე. ამ შემთხვევაში, არსებობს გამოვლინება პრევენციული მულტირედირება დროის გაჭრის გზით.ამ შემზარავი ტერმინის მნიშვნელობა ქვემოთ არის განმარტებული.

ბრინჯი 10.2. ძაფებს შორის გადართვა მარტივი მრავალსიდიანი პროგრამით

ძაფების შეწყვეტისას და სხვა ძაფებზე კონტროლის გადაცემისას, ოპერაციული სისტემა იყენებს პრევენციული მულტილადირების პრინციპს დროის ნაჭრების საშუალებით. დროის კვანტიზაცია ასევე წყვეტს ერთ -ერთ ჩვეულებრივ პრობლემას, რომელიც წარმოიშვა ადრე მრავალმხრივ პროგრამებში - ერთი ძაფი იკავებს პროცესორის მთელ დროს და არ ჩამოუვარდება სხვა ძაფების კონტროლს (როგორც წესი, ეს ხდება ინტენსიურ ციკლში, როგორც ზემოთ). პროცესორის ექსკლუზიური გატაცების თავიდან ასაცილებლად, თქვენმა თემებმა დროდადრო უნდა გადასცეს კონტროლი სხვა ძაფებზე. თუ პროგრამა აღმოჩნდება "არაცნობიერი", არსებობს სხვა, ოდნავ ნაკლებად სასურველი გამოსავალი: ოპერაციული სისტემა ყოველთვის წინ უსწრებს გაშვებულ ძაფს, მიუხედავად მისი პრიორიტეტული დონისა, ისე რომ პროცესორზე წვდომა მიენიჭოს სისტემის ყველა ძაფს.

ვინაიდან .NET– ის ყველა ვერსიის კვანტიზაციის სქემები, რომლებიც მუშაობს .NET– ს აქვს მინიმალური დროის მონაკვეთი თითოეული ძაფისთვის, .NET პროგრამირების შემთხვევაში, CPU– ს ექსკლუზიურ ხელში ჩაგდების პრობლემები არც ისე სერიოზულია. მეორეს მხრივ, თუ .NET ჩარჩო ოდესმე ადაპტირებულია სხვა სისტემებისთვის, ეს შეიძლება შეიცვალოს.

თუ ჩვენ ჩავრთავთ შემდეგ სტრიქონს ჩვენს პროგრამაში დარეკვის დაწყებამდე, მაშინ ყველაზე დაბალი პრიორიტეტის მქონე ძაფებიც კი მიიღებენ პროცესორის დროის გარკვეულ ნაწილს:

bThread.Priority = ThreadPriority.Highest

ბრინჯი 10.3. ძაფი უმაღლესი პრიორიტეტით ჩვეულებრივ უფრო სწრაფად იწყებს მუშაობას

ბრინჯი 10.4. პროცესორი ასევე გათვალისწინებულია დაბალი პრიორიტეტული ძაფებისთვის

ბრძანება ანიჭებს მაქსიმალურ პრიორიტეტს ახალ ძაფს და ამცირებს მთავარი ძაფის პრიორიტეტს. ნახ. 10.3 ჩანს, რომ ახალი ძაფი იწყებს მუშაობას უფრო სწრაფად, ვიდრე ადრე, მაგრამ, როგორც ნახ. 10.4, მთავარი ძაფი ასევე იღებს კონტროლსსიზარმაცე (თუმცა ძალიან მცირე ხნით და მხოლოდ ნაკადის გახანგრძლივებული მუშაობის შემდეგ გამოკლებით). როდესაც პროგრამას აწარმოებთ თქვენს კომპიუტერებზე, თქვენ მიიღებთ მსგავს შედეგებს, რაც ნაჩვენებია ნახატზე. 10.3 და 10.4, მაგრამ ჩვენს სისტემებს შორის განსხვავებების გამო, ზუსტი შესატყვისი არ იქნება.

ThreadPrlority ჩამოთვლილი ტიპი მოიცავს ღირებულებებს ხუთი პრიორიტეტული დონისთვის:

თემა პრიორიტეტი. უმაღლესი

ThreadPriority.AboveNormal

ThreadPrlority. ნორმალური

ThreadPriority.BelowNormal

თემა პრიორიტეტი. ყველაზე დაბალი

გაწევრიანების მეთოდი

ზოგჯერ პროგრამის თემა უნდა შეჩერდეს სხვა თემის დასრულებამდე. დავუშვათ, რომ გსურთ შეწყვიტოთ ძაფი 1 მანამ, სანამ ძაფი 2 არ გამოთვლის. Ამისთვის ნაკადიდან 1 Join მეთოდი ეწოდება ნაკადს 2. სხვა სიტყვებით რომ ვთქვათ, ბრძანებას

ძაფი 2. შეერთება ()

აჩერებს მიმდინარე ძაფს და ელოდება ძაფის დასრულებას 2. თემა 1 გადადის ჩაკეტილი მდგომარეობა.

თუ ნაკადს შეუერთდებით ნაკადს 2 ნაკადი Join მეთოდის გამოყენებით, ოპერაციული სისტემა ავტომატურად დაიწყებს ნაკადს 1 ნაკადის შემდეგ. გაითვალისწინეთ, რომ გაშვების პროცესი არის არადეტერმინისტული:შეუძლებელია ზუსტად ითქვას, რამდენი ხნის შემდეგ დასრულდება ძაფი 2, ძაფი 1 დაიწყება მუშაობა. არსებობს Join– ის კიდევ ერთი ვერსია, რომელიც ბულუს მნიშვნელობას აბრუნებს:

თემა 2. შეერთება (მთელი რიცხვი)

ეს მეთოდი ან ელოდება ძაფი 2 -ს დასრულებას, ან ბლოკავს ძაფს 1 განსაზღვრული დროის ინტერვალის გასვლის შემდეგ, რის შედეგადაც ოპერაციული სისტემის გრაფიკმა კვლავ გამოყოს პროცესორის დრო ძაფზე. მეთოდი აბრუნებს True- ს, თუ ძაფი 2 მთავრდება მითითებული დროის ინტერვალის ამოწურვამდე და სხვაგვარად False.

დაიმახსოვრე ძირითადი წესი: დაასრულა თუ ამოიწურა თემა 2, თქვენ არ გაქვთ კონტროლი როდის გააქტიურდება თემა 1.

ძაფების სახელები, CurrentThread და ThreadState

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- ებს. მომდევნო სვეტში ჩამოთვლილია ნაკადის სახელები (თუ მინიჭებულია). მდებარეობის სვეტი მიუთითებს გაშვების პროცედურას (მაგალითად, კონსოლის კლასის WriteLine პროცედურა, სურათი 10.5). დანარჩენი სვეტები შეიცავს ინფორმაციას პრიორიტეტული და შეჩერებული ძაფების შესახებ (იხ. შემდეგი ნაწილი).

ძაფის ფანჯარა (არა ოპერაციული სისტემა!) გაძლევთ საშუალებას აკონტროლოთ თქვენი პროგრამის ძაფები კონტექსტური მენიუს გამოყენებით. მაგალითად, შეგიძლიათ შეაჩეროთ მიმდინარე ძაფი შესაბამის ხაზზე მარჯვენა ღილაკით და აირჩიოთ Freeze ბრძანება (მოგვიანებით, გაჩერებული ძაფის განახლება შესაძლებელია). ძაფების შეჩერება ხშირად გამოიყენება გამართვისას, რათა თავიდან აიცილოთ გაუმართავი ძაფის აპლიკაციაში ჩარევა. გარდა ამისა, ნაკადების ფანჯარა საშუალებას გაძლევთ გაააქტიუროთ სხვა (არ შეწყვეტილი) ნაკადი; ამისათვის დააწკაპუნეთ მარჯვენა ღილაკით საჭირო ხაზზე და კონტექსტური მენიუდან აირჩიეთ ბრძანება Switch To Thread (ან უბრალოდ ორჯერ დააწკაპუნეთ ძაფის ხაზზე). როგორც ქვემოთ იქნება ნაჩვენები, ეს ძალზე სასარგებლოა პოტენციური ჩიხების დიაგნოსტიკაში.

ნაკადის შეჩერება

დროებით გამოუყენებელი ნაკადები შეიძლება გადავიდეს პასიურ მდგომარეობაში Slеer მეთოდის გამოყენებით. ჩაკეტილად ითვლება პასიური ნაკადიც. რასაკვირველია, როდესაც ძაფი მოთავსებულია პასიურ მდგომარეობაში, დანარჩენ ძაფებს ექნებათ მეტი პროცესორის რესურსი. Slеer მეთოდის სტანდარტული სინტაქსი ასეთია: თემა. ძილი (ინტერვალი_ მილიწამებში)

ძილის ზარის შედეგად, აქტიური ძაფი ხდება პასიური მინიმუმ განსაზღვრული რაოდენობის მილიწამში (თუმცა გააქტიურება მითითებული ინტერვალის ამოწურვისთანავე არ არის გარანტირებული). გთხოვთ გაითვალისწინოთ: მეთოდის გამოძახებისას, კონკრეტული ძაფის მითითება არ გადაიცემა - ძილის მეთოდი იწოდება მხოლოდ აქტიური ძაფისთვის.

Sleep– ის სხვა ვერსია ახლანდელ თემას უთმობს CPU– ს გამოყოფილ დანარჩენ დროს:

თემა ძილი (0)

შემდეგი ვარიანტი აყენებს მიმდინარე ძაფს პასიურ მდგომარეობაში შეუზღუდავი დროით (გააქტიურება ხდება მხოლოდ მაშინ, როდესაც თქვენ დარეკავთ Interrupt):

თემა. სლაერი (დრო ამოწურულია შეუზღუდავი)

ვინაიდან პასიური ძაფები (თუნდაც შეუზღუდავი ვადით) შეიძლება შეწყდეს შეწყვეტის მეთოდით, რაც გამონაკლისის გარეშე იწვევს ThreadlnterruptExcepti– ს დაწყებას, Slayer ზარი ყოველთვის ჩასმულია Try-Catch ბლოკში, როგორც შემდეგ ფრაგმენტში:

სცადე

თემა. ძილი (200)

”ძაფის პასიური მდგომარეობა შეწყდა

დაიჭირე e როგორც გამონაკლისი

"სხვა გამონაკლისები

დაასრულე ცდა

ყოველი .NET პროგრამა მუშაობს პროგრამის ძაფზე, ამიტომ ძილის მეთოდი ასევე გამოიყენება პროგრამების შესაჩერებლად (თუ პროგრამის მიერ Threadipg სახელების სივრცე არ არის იმპორტირებული, თქვენ უნდა გამოიყენოთ სრულად კვალიფიციური სახელი Threading. თემა. ძილი).

პროგრამის ძაფების შეწყვეტა ან შეწყვეტა

ძაფი ავტომატურად წყდება, როდესაც მითითებულია მეთოდი, როდესაც იქმნება ThreadStart დელეგატი, მაგრამ ხანდახან აუცილებელია მეთოდის (და, შესაბამისად, ძაფის) შეწყვეტა, როდესაც ხდება გარკვეული ფაქტორები. ასეთ შემთხვევებში, ნაკადები ჩვეულებრივ ამოწმებენ პირობითი ცვლადი,რომლის მდგომარეობიდან გამომდინარემიიღება გადაწყვეტილება ნაკადულიდან საგანგებო გასვლის შესახებ. როგორც წესი, Do-while მარყუჟი შედის პროცედურაში:

Sub ThreadedMethod ()

„პროგრამამ უნდა უზრუნველყოს გამოკითხვის საშუალება

"პირობითი ცვლადი.

”მაგალითად, პირობითი ცვლადი შეიძლება ჩამოყალიბდეს როგორც თვისება

Do while conditionVariable = False And MoreWorkToDo

"მთავარი კოდი

Loop End Sub

გარკვეული დრო სჭირდება პირობითი ცვლადის გამოკითხვას. თქვენ უნდა გამოიყენოთ მუდმივი გამოკითხვა მარყუჟის მდგომარეობაში, თუ ელოდებით თემის ნაადრევად დასრულებას.

თუ მდგომარეობის ცვლადი უნდა შემოწმდეს კონკრეტულ ადგილას, გამოიყენეთ If-Then ბრძანება Exit Sub– თან ერთად უსასრულო მარყუჟში.

პირობითი ცვლადის წვდომა უნდა იყოს სინქრონიზებული ისე, რომ სხვა ძაფებიდან გამოვლენამ ხელი არ შეუშალოს მის ნორმალურ გამოყენებას. ეს მნიშვნელოვანი თემა დაფარულია "პრობლემების მოგვარება: სინქრონიზაცია" განყოფილებაში.

სამწუხაროდ, პასიური (ან სხვაგვარად დაბლოკილი) ძაფების კოდი არ არის შესრულებული, ამიტომ პირობითი ცვლადის გამოკითხვის ვარიანტი მათთვის შესაფერისი არ არის. ამ შემთხვევაში, დარეკეთ Interrupt მეთოდი ობიექტზე ცვლადზე, რომელიც შეიცავს მითითებას სასურველ ძაფზე.

შეწყვეტის მეთოდის გამოყენება შესაძლებელია მხოლოდ ლოდინის, ძილის ან შეერთების მდგომარეობის ძაფებზე. თუ თქვენ დარეკავთ Interrupt ძაფისთვის, რომელიც არის ერთ -ერთ ჩამოთვლილ მდგომარეობაში, მაშინ გარკვეული პერიოდის შემდეგ ძაფი კვლავ დაიწყებს მუშაობას და შესრულების გარემო იწყებს ThreadlnterruptExcepti გამონაკლისის გარდა ძაფში. ეს ხდება მაშინაც კი, თუ ძაფი გახდება პასიურად განუსაზღვრელი ვადით Thread.Sleepdimeout. უსასრულო). ჩვენ ვამბობთ "ცოტა ხნის შემდეგ", რადგან ძაფების დაგეგმვა არ არის განსაზღვრული. გამონაკლისის გარდა, ThreadlnterruptExcepti დაჭერილია Catch განყოფილებაში, რომელიც შეიცავს გასვლის კოდს ლოდინის მდგომარეობიდან. ამასთან, Catch მონაკვეთს არ უნდა შეწყვიტოს ძაფი შეწყვეტის ზარზე - ძაფი ამუშავებს გამონაკლისს ისე, როგორც საჭიროდ მიიჩნევს.

.NET– ში, შეწყვეტის მეთოდს შეიძლება ვუწოდოთ განბლოკილი ძაფებისთვისაც კი. ამ შემთხვევაში, ძაფი წყდება უახლოეს ბლოკირებაზე.

ძაფების შეჩერება და მოკვლა

Threading სახელების სივრცე შეიცავს სხვა მეთოდებს, რომლებიც წყვეტს ნორმალურ ძაფს:

  • შეაჩეროს;
  • Გაუქმება.

ძნელი სათქმელია, თუ რატომ .NET მოიცავდა ამ მეთოდების მხარდაჭერას - Suspend და Abort დარეკვა, სავარაუდოდ, გამოიწვევს პროგრამის არასტაბილურობას. არცერთი მეთოდი არ იძლევა ნაკადის ნორმალურ დენიტიალიზაციას. გარდა ამისა, შეჩერების ან შეწყვეტისას დარეკვისას შეუძლებელია იმის პროგნოზირება, თუ რა მდგომარეობაში დატოვებს ძაფს საგნები შეჩერების ან შეწყვეტის შემდეგ.

აბორტის გამოძახება აგდებს ThreadAbortException. იმის გასაგებად, თუ რატომ არ უნდა იქნას გამოყენებული ამ უცნაური გამონაკლისი პროგრამებში, აქ არის ნაწყვეტი .NET SDK დოკუმენტაციიდან:

”... როდესაც ძაფი იშლება აბორტის გამოძახებით, გაშვების დრო აგდებს ThreadAbortException. ეს არის განსაკუთრებული სახის გამონაკლისი, რომლის დაჭერა შეუძლებელია პროგრამის მიერ. როდესაც ეს გამონაკლისი გამოჩნდება, გაშვების დრო გადის ყველა საბოლოოდ ბლოკზე ძაფის შეწყვეტამდე. იმის გამო, რომ ნებისმიერი ქმედება შეიძლება განხორციელდეს საბოლოოდ ბლოკებში, დარეკეთ Join– ში, რომ ნაკადი განადგურდეს. ”

მორალი: შეწყვეტა და შეჩერება არ არის რეკომენდებული (და თუ თქვენ მაინც არ შეგიძლიათ შეჩერების გარეშე, განაახლოთ შეჩერებული ძაფი განახლების მეთოდის გამოყენებით). თქვენ შეგიძლიათ უსაფრთხოდ შეწყვიტოთ თემა მხოლოდ სინქრონიზებული მდგომარეობის ცვლადის გამოკითხვით ან ზემოთ განხილული შეწყვეტის მეთოდის გამოძახებით.

ფონის ძაფები (დემონები)

ფონზე გაშვებული ზოგიერთი თემა ავტომატურად წყვეტს გაშვებას პროგრამის სხვა კომპონენტების გაჩერებისას. კერძოდ, ნაგვის შემგროვებელი გადის ერთ -ერთ ფონის ძაფში. ფონის ძაფები ჩვეულებრივ იქმნება მონაცემების მისაღებად, მაგრამ ეს კეთდება მხოლოდ იმ შემთხვევაში, თუ სხვა თემაში გაშვებულია კოდი, რომელსაც შეუძლია მიღებული მონაცემების დამუშავება. სინტაქსი: ნაკადის სახელი. IsBackGround = ჭეშმარიტი

თუ აპლიკაციაში დარჩა მხოლოდ ფონის ძაფები, პროგრამა ავტომატურად წყდება.

უფრო სერიოზული მაგალითი: მონაცემების ამოღება HTML კოდიდან

ჩვენ გირჩევთ გამოიყენოთ ნაკადები მხოლოდ მაშინ, როდესაც პროგრამის ფუნქციონირება აშკარად იყოფა რამდენიმე ოპერაციად. კარგი მაგალითია HTML მოპოვების პროგრამა მე –9 თავში. ჩვენი კლასი აკეთებს ორ რამეს: მონაცემების მოპოვებას ამაზონიდან და დამუშავებას. ეს არის სრულყოფილი მაგალითი იმ სიტუაციისა, რომელშიც მრავალძირიანი პროგრამირება ნამდვილად მიზანშეწონილია. ჩვენ ვქმნით კლასებს რამდენიმე განსხვავებული წიგნისთვის და შემდეგ ვაანალიზებთ მონაცემებს სხვადასხვა ნაკადში. თითოეული წიგნისთვის ახალი ძაფის შექმნა ზრდის პროგრამის ეფექტურობას, რადგან სანამ ერთი ძაფი იღებს მონაცემებს (რასაც შეიძლება მოითხოვოს ამაზონის სერვერზე ლოდინი), მეორე თემა დაკავებული იქნება უკვე მიღებული მონაცემების დამუშავებით.

ამ პროგრამის მრავალ ხრახნიანი ვერსია მუშაობს უფრო ეფექტურად, ვიდრე ერთი ხრახნიანი ვერსია მხოლოდ კომპიუტერზე, რომელსაც აქვს რამდენიმე პროცესორი ან თუ დამატებითი მონაცემების მიღება შეიძლება ეფექტურად იყოს შერწყმული მათ ანალიზთან.

როგორც ზემოთ აღვნიშნეთ, მხოლოდ პროცედურები, რომლებსაც არ გააჩნიათ პარამეტრები, შეიძლება გაშვებული იქნას ძაფებში, ასე რომ თქვენ მოგიწევთ პროგრამის უმნიშვნელო ცვლილებების შეტანა. ქვემოთ მოცემულია ძირითადი პროცედურა, გადაწერილი პარამეტრების გამორიცხვის მიზნით:

საჯარო ქვე FindRank ()

m_Rank = ScrapeAmazon ()

Console.WriteLine ("წოდება" & m_Name & "არის" & GetRank)

დასასრული ქვე

ვინაიდან ჩვენ ვერ შევძლებთ ინფორმაციის შენახვასა და მოძიებას კომბინირებული ველით (გრაფიკული ინტერფეისით მრავალძაფრიანი პროგრამების წერა განხილულია ამ თავის ბოლო ნაწილში), პროგრამა ინახავს მასივში ოთხი წიგნის მონაცემებს, რომლის განმარტება იწყება ასე:

Dim TheBook (3.1) როგორც სიმებიანი წიგნი (0.0) = "1893115992"

theBook (0.l) = "VB .NET პროგრამირება" და ა.შ.

ოთხი ნაკადი იქმნება იმავე ციკლში, რომელშიც იქმნება AmazonRanker ობიექტები:

I = 0 -დან 3 -მდე

სცადე

theRanker = ახალი AmazonRanker (წიგნი (i.0). theBookd.1))

aThreadStart = ახალი ThreadStar (AddressOf theRanker.FindRan ()

aThread = ახალი თემა (aThreadStart)

aThread.Name = წიგნი (i.l)

aThread.Start () Catch e As Exception

Console.WriteLine (e.Message)

დაასრულე ცდა

შემდეგი

ქვემოთ მოცემულია პროგრამის სრული ტექსტი:

ვარიანტი მკაცრი იმპორტის სისტემაში. IO იმპორტი System.Net

იმპორტის სისტემა. თემა

მოდულის მოდული

ქვე ძირითადი ()

დაბნეული წიგნი (3.1) როგორც სიმებიანი

წიგნი (0.0) = "1893115992"

theBook (0.l) = "VB .NET პროგრამირება"

წიგნი (l.0) = "1893115291"

theBook (l.l) = "მონაცემთა ბაზის პროგრამირება VB .NET"

წიგნი (2,0) = "1893115623"

theBook (2.1) = "პროგრამისტის" შესავალი C # - ში. "

წიგნი (3.0) = "1893115593"

theBook (3.1) = "ჯირკვლის. NET პლატფორმა"

Dim i როგორც მთელი რიცხვი

Dim theRanker როგორც = AmazonRanker

Dim aThreadStart As Threading.ThreadStart

Dim aThread As Threading. თემა

I = 0 -დან 3 -მდე

სცადე

theRanker = ახალი AmazonRankerttheBook (i.0). წიგნი (i.1))

aThreadStart = ახალი ThreadStart (AddressRanker. FindRank)

aThread = ახალი თემა (aThreadStart)

aThread.Name = წიგნი (i.l)

aThread.Start ()

დაიჭირე e როგორც გამონაკლისი

Console.WriteLlnete.Message)

დასრულება სცადე შემდეგი

Console.ReadLine ()

დასასრული ქვე

დასრულების მოდული

საზოგადოებრივი კლასის AmazonRanker

პირადი m_URL როგორც სიმებიანი

პირადი m_Rank როგორც მთელი რიცხვი

პირადი m_Name როგორც სიმებიანი

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

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

m_Name = the Name End Sub

საჯარო ქვე FindRank () m_Rank = ScrapeAmazon ()

Console.Writeline ("წოდება" & m_Name & "არის"

& GetRank) ბოლო ქვე

Public Readonly Property GetRank () As String Get

თუ m_Rank<>0 მაშინ

დააბრუნე CStr (m_Rank) სხვა

"პრობლემები

Დაასრულე თუ

დასრულება მიიღეთ

თვისების დასრულება

Public Readonly Property GetName () როგორც სიმებიანი მიიღეთ

M_Name- ის დაბრუნება

დასრულება მიიღეთ

თვისების დასრულება

პირადი ფუნქცია ScrapeAmazon () როგორც მთელი რიცხვი სცადეთ

შეანელეთ UURL როგორც ახალი ური (m_URL)

Dim theRequest როგორც WebRequest

theRequest = WebRequest. შექმნა (theURL)

Dim theResponse როგორც WebResponse

theResponse = theRequest.GetResponse

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

Dim მონაცემების სიმებიანი

theData = aReader.ReadToEnd

დაბრუნების ანალიზი (theData)

დაიჭირე E როგორც გამონაკლისი

Console.WriteLine (E.Message)

Console.WriteLine (E.StackTrace)

კონსოლი ReadLine ()

დასრულება სცადეთ დასრულების ფუნქცია

პირადი ფუნქციის ანალიზი (ByVal theData As String) როგორც მთელი რიცხვი

Dim Location As.Integer მდებარეობა = theData.IndexOf (" Amazon.com

გაყიდვების წოდება:") _

+ "Amazon.com გაყიდვების რანგი:".სიგრძე

Dim temp როგორც სიმებიანი

გააკეთე სანამ theData.Substring (Location.l) = "<" temp = temp

& theData.Substring (საიდან. l) მდებარეობა + = 1 მარყუჟი

დაბრუნება Clnt (ტემპერატურა)

დასრულების ფუნქცია

დასასრულის კლასი

მრავალძირიანი ოპერაციები ჩვეულებრივ გამოიყენება .NET და I / O სახელების სივრცეებში, ამიტომ .NET Framework ბიბლიოთეკა უზრუნველყოფს მათ სპეციალურ ასინქრონულ მეთოდებს. ასტიქრონული მეთოდების გამოყენების შესახებ დამატებითი ინფორმაციისათვის მრავალსართულიანი პროგრამების წერისას იხილეთ HTTPWebRequest კლასის BeginGetResponse და EndGetResponse მეთოდები.

ძირითადი საფრთხე (ზოგადი მონაცემები)

ჯერჯერობით, განიხილება ძაფების ერთადერთი უსაფრთხო შემთხვევა - ჩვენმა ნაკადებმა არ შეცვალა ზოგადი მონაცემები.თუ თქვენ დაუშვებთ ცვლილებას ზოგად მონაცემებში, პოტენციური შეცდომები იწყებს ექსპონენციალურად გამრავლებას და ბევრად უფრო ძნელი ხდება მათი მოშორება პროგრამისთვის. მეორეს მხრივ, თუ თქვენ კრძალავთ გაზიარებული მონაცემების შეცვლას სხვადასხვა ძაფებით, მრავალსიდიანი .NET პროგრამირება ძნელად განსხვავდება VB6- ის შეზღუდული შესაძლებლობებისგან.

ჩვენ გვინდა თქვენი ყურადღება გავამახვილოთ პატარა პროგრამაზე, რომელიც აჩვენებს პრობლემებს, რომლებიც წარმოიქმნება ზედმეტი დეტალების გარეშე. ეს პროგრამა ასახავს სახლს თერმოსტატით თითოეულ ოთახში. თუ ტემპერატურა 5 გრადუსი ფარენჰეიტით ან მეტი (დაახლოებით 2.77 გრადუსი ცელსიუსით) ნაკლებია მიზნობრივ ტემპერატურაზე, ჩვენ ვუბრძანებთ გათბობის სისტემას ტემპერატურის გაზრდა 5 გრადუსით; წინააღმდეგ შემთხვევაში, ტემპერატურა იზრდება მხოლოდ 1 გრადუსით. თუ ამჟამინდელი ტემპერატურა აღემატება ან ტოლია მითითებულს, არანაირი ცვლილება არ ხდება. თითოეულ ოთახში ტემპერატურის კონტროლი ხორციელდება ცალკეული ნაკადის საშუალებით 200 მილიწამი დაგვიანებით. ძირითადი სამუშაო შესრულებულია შემდეგი ფრაგმენტით:

თუ mHouse.HouseTemp< mHouse.MAX_TEMP = 5 Then Try

თემა. ძილი (200)

Catch tie As ThreadlnterruptionException

”პასიური ლოდინი შეწყდა

დაიჭირე e როგორც გამონაკლისი

„სხვა მხრივ სცადეთ გამონაკლისები

mHouse.HouseTemp + - 5 "და ა.შ.

ქვემოთ მოცემულია პროგრამის სრული წყარო. შედეგი ნაჩვენებია ნახ. 10.6: ტემპერატურა სახლში მიაღწია 105 გრადუსს ფარენჰეიტში (40.5 გრადუსი ცელსიუსი)!

1 ვარიანტი მკაცრად ჩართულია

2 იმპორტის სისტემა. თემა

3 მოდულის მოდული

4 ქვე ძირითადი ()

5 Dim myHouse As New House (l0)

6 კონსოლი. ReadLine ()

7 დასასრული ქვე

8 ბოლო მოდული

9 საჯარო კლასის სახლი

10 Public Const MAX_TEMP როგორც მთელი რიცხვი = 75

11 პირადი mCurTemp როგორც მთელი რიცხვი = 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 იყიდება i = 0 ნომრების ოთახებში -1

19 სცადე

20 mRooms (i) = NewRoom (Me, mCurTemp, CStr (i) & "throom")

21 aThreadStart - ახალი ThreadStart (AddressOf _

ოთახები (i) .CheckTempInRoom)

22 aThread = ახალი თემა (aThreadStart)

23 a თემა, დაწყება ()

24 დაიჭირე E როგორც გამონაკლისი

25 Console.WriteLine (E.StackTrace)

26 დაასრულე ცდა

27 შემდეგი

28 დასასრული ქვე

29 საჯარო საკუთრების HouseTemp () როგორც მთელი რიცხვი

ოცდაათი მიიღეთ

31 დაბრუნება mCurTemp

32 დასასრულის მიღება

33 კომპლექტი (ByVal მნიშვნელობა, როგორც მთელი რიცხვი)

34 mCurTemp = მნიშვნელობა 35 ბოლო კომპლექტი

36 ბოლო ქონება

37 ბოლო კლასი

38 საჯარო კლასის ოთახი

39 პირადი mCurTemp როგორც მთელი რიცხვი

40 პირადი mName As String

41 კერძო mHouse As House

42 Public Sub New (ByVal theHouse As House,

ByVal temp როგორც მთელი რიცხვი, ByVal ოთახის სახელი როგორც სიმებიანი)

43 mHouse = სახლი

44 mCurTemp = ტემპერატურა

45 მ სახელი = ოთახის სახელი

46 დასასრულის ქვე

47 საჯარო ქვე -შემოწმება TempInRoom ()

48 ტემპერატურის ცვლა ()

49 დასასრულის ქვე

50 პირადი ქვე -ცვლილება ტემპერატურა ()

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 დაიჭირე e როგორც გამონაკლისი

70 "სხვა გამონაკლისები

71 დაასრულე ცდა

72 დასასრულის ქვე

73 ბოლო კლასი

ბრინჯი 10.6. მრავალსიდიანი საკითხები

Sub Main პროცედურა (სტრიქონები 4-7) ქმნის "სახლს" ათი "ოთახით". ჰაუს კლასი ადგენს მაქსიმალურ ტემპერატურას 75 გრადუსი ფარენჰეიტით (დაახლოებით 24 გრადუსი ცელსიუსით). 13-28 სტრიქონები განსაზღვრავს საკმაოდ რთულ სახლის კონსტრუქტორს. პროგრამის გაგების გასაღები არის სტრიქონები 18-27. მე -20 სტრიქონი ქმნის ოთახის სხვა ობიექტს, ხოლო სახლის ობიექტზე მითითება გადადის კონსტრუქტორზე, რათა ოთახის ობიექტმა საჭიროების შემთხვევაში მიმართოს მას. ხაზები 21-23 იწყებს ათი ნაკადს, რათა შეცვალოს ტემპერატურა თითოეულ ოთახში. ოთახის კლასი განისაზღვრება 38-73 სტრიქონებზე. House coxpa მითითებაინახება mHouse ცვლადში Room კლასის კონსტრუქტორში (სტრიქონი 43). ტემპერატურის შემოწმებისა და რეგულირების კოდი (სტრიქონები 50-66) გამოიყურება მარტივი და ბუნებრივი, მაგრამ როგორც მალე ნახავთ, ეს შთაბეჭდილება მატყუებს! გაითვალისწინეთ, რომ ეს კოდი გახვეულია Try-Catch ბლოკში, რადგან პროგრამა იყენებს ძილის მეთოდს.

თითქმის არავინ დათანხმდება იცხოვროს 105 გრადუსი ფარენჰეიტის ტემპერატურაზე (40.5 დან 24 გრადუსამდე ცელსიუსამდე). Რა მოხდა? პრობლემა დაკავშირებულია შემდეგ ხაზთან:

თუ mHouse.HouseTemp< mHouse.MAX_TEMP - 5 Then

და ხდება შემდეგი: პირველი, ტემპერატურა შემოწმებულია ნაკადის 1. ის ხედავს, რომ ტემპერატურა ძალიან დაბალია და ზრდის მას 5 გრადუსით. სამწუხაროდ, სანამ ტემპერატურა მოიმატებს, ნაკადი 1 წყდება და კონტროლი გადადის ნაკადზე 2. ნაკადი 2 ამოწმებს იმავე ცვლადს ჯერ არ შეცვლილანაკადი 1. ამრიგად, ნაკადი 2 ასევე ემზადება ტემპერატურის 5 გრადუსით ასამაღლებლად, მაგრამ ამის დრო არ აქვს და ასევე გადადის ლოდინის მდგომარეობაში. პროცესი გრძელდება მანამ, სანამ ნაკადი 1 არ გააქტიურდება და გადადის შემდეგ ბრძანებაზე - ტემპერატურის გაზრდა 5 გრადუსით. ზრდა მეორდება, როდესაც ათივე ნაკადი გააქტიურდება და სახლის მაცხოვრებლებს ცუდი დრო ექნებათ.

პრობლემის გადაწყვეტა: სინქრონიზაცია

წინა პროგრამაში იქმნება სიტუაცია, როდესაც პროგრამის გამომუშავება დამოკიდებულია ძაფების შესრულების წესზე. მისგან თავის დასაღწევად, თქვენ უნდა დარწმუნდეთ, რომ ბრძანებებს მოსწონთ

თუ mHouse.HouseTemp< mHouse.MAX_TEMP - 5 Then...

სრულად არის დამუშავებული აქტიური ძაფით, სანამ ის შეწყდება. ამ ქონებას ქვია ატომური სირცხვილი -კოდის ბლოკი უნდა შესრულდეს თითოეული ძაფით შეფერხების გარეშე, როგორც ატომური ერთეული. ბრძანებების ჯგუფს, ატომურ ბლოკში გაერთიანებული, ძაფის განმსაზღვრელი ვერ შეწყვეტს მის დასრულებამდე. ნებისმიერი მრავალსიდიანი პროგრამირების ენას აქვს თავისი გზები ატომურობის უზრუნველსაყოფად. VB .NET– ში SyncLock ბრძანების გამოსაყენებლად უმარტივესი გზაა ობიექტის ცვლადში გადასვლა, როდესაც დარეკავთ. მცირე ცვლილებები შეიტანეთ ChangeTemperature პროცედურაში წინა მაგალითიდან და პროგრამა კარგად იმუშავებს:

პირადი Sub 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 ბრძანების სწორად გამოყენება ინახავს თქვენი პროგრამის ძაფს უსაფრთხოდ. სამწუხაროდ, SyncLock– ის გადაჭარბებული გამოყენება უარყოფითად აისახება შესრულებაზე. კოდის სინქრონიზაცია მრავალძირიან პროგრამაში ამცირებს მისი მუშაობის სიჩქარეს რამდენჯერმე. მოახდინეთ მხოლოდ ყველაზე საჭირო კოდის სინქრონიზაცია და რაც შეიძლება მალე გაათავისუფლეთ საკეტი.

საბაზისო კოლექციის კლასები სახიფათოა მრავალ ხრახნიან პროგრამებში, მაგრამ .NET Framework მოიცავს კოლექციის კლასების უმეტესობის ძაფით დაცულ ვერსიებს. ამ კლასებში პოტენციურად საშიში მეთოდების კოდი მოთავსებულია SyncLock ბლოკებში. საკოლექციო კლასების ძაფით დაცული ვერსიები უნდა იქნას გამოყენებული მრავალსართულიან პროგრამებში, სადაც მონაცემთა მთლიანობა შელახულია.

რჩება აღვნიშნო, რომ პირობითი ცვლადები ადვილად ხორციელდება SyncLock ბრძანების გამოყენებით. ამისათვის თქვენ უბრალოდ უნდა მოახდინოთ წერის სინქრონიზაცია საერთო ლოგიკური თვისებისთვის, რომელიც ხელმისაწვდომია კითხვისა და წერისათვის, როგორც ეს კეთდება შემდეგ ფრაგმენტში:

საჯარო კლასის მდგომარეობა ცვალებადი

პირადი გაზიარებული საკეტი როგორც ობიექტი = ახალი ობიექტი ()

პირადი გაზიარებული mOK როგორც ლოგიკური გაზიარებული

თვისება TheConditionVariable () როგორც ლოგიკური

მიიღეთ

MOK დაბრუნება

დასრულება მიიღეთ

დაყენება (ByVal Value As Boolean) SyncLock (მბრძანებელი)

mOK = მნიშვნელობა

დაასრულეთ SyncLock

დასრულების ნაკრები

თვისების დასრულება

დასასრულის კლასი

SyncLock ბრძანება და მონიტორის კლასი

SyncLock ბრძანების გამოყენება მოიცავს ზოგიერთ დახვეწილობას, რომლებიც არ იყო ნაჩვენები ზემოთ მოცემულ უბრალო მაგალითებში. ასე რომ, სინქრონიზაციის ობიექტის არჩევანი ძალიან მნიშვნელოვან როლს ასრულებს. სცადეთ გაუშვათ წინა პროგრამა SyncLock (Me) ბრძანებით SyncLock (mHouse) ნაცვლად. ტემპერატურა კვლავ იზრდება ზღურბლზე მაღლა!

გახსოვდეთ, რომ SyncLock ბრძანება სინქრონიზებულია გამოყენებით ობიექტი,გადაეცა როგორც პარამეტრი და არა კოდის ფრაგმენტი. SyncLock პარამეტრი მოქმედებს როგორც სხვა ძაფებიდან სინქრონიზებული ფრაგმენტის წვდომის კარი. SyncLock (Me) ბრძანება ფაქტობრივად ხსნის რამდენიმე განსხვავებულ "კარს", რაც ზუსტად თავიდან აიცილეთ სინქრონიზაციით. მორალი:

მრავალმხრივი პროგრამის გაზიარებული მონაცემების დასაცავად, SyncLock ბრძანებამ ერთდროულად უნდა მოახდინოს ერთი ობიექტის სინქრონიზაცია.

ვინაიდან სინქრონიზაცია დაკავშირებულია კონკრეტულ ობიექტთან, ზოგიერთ სიტუაციაში შესაძლებელია სხვა ფრაგმენტების უნებლიედ ჩაკეტვა. ვთქვათ, თქვენ გაქვთ ორი სინქრონიზებული მეთოდი, პირველი და მეორე, და ორივე მეთოდი სინქრონიზებულია bigLock ობიექტზე. როდესაც ძაფი 1 შედის მეთოდით პირველში და იტევს bigLock- ს, ვერანაირი ძაფი ვერ შეძლებს მეთოდის მეორეში შეყვანას, რადგან მასზე წვდომა უკვე შეზღუდულია 1 ძაფზე!

SyncLock ბრძანების ფუნქციონალურობა შეიძლება ჩაითვალოს მონიტორის კლასის ფუნქციონირების ქვეჯგუფად. მონიტორის კლასი არის ძალიან პერსონალურად მორგებული და მისი გამოყენება შესაძლებელია არა ტრივიალური სინქრონიზაციის ამოცანების გადასაჭრელად. SyncLock ბრძანება არის Moni tor კლასის Enter და Exi t მეთოდების სავარაუდო ანალოგი:

სცადე

მონიტორი. შეიყვანეთ (theObject) საბოლოოდ

მონიტორი. გასვლა (theObject)

დაასრულე ცდა

ზოგიერთი სტანდარტული ოპერაციისთვის (ცვლადის გაზრდა / შემცირება, ორი ცვლადის შინაარსის გაცვლა), .NET Framework უზრუნველყოფს ჩაკეტილ კლასს, რომლის მეთოდები ასრულებენ ამ ოპერაციებს ატომურ დონეზე. Interlocked კლასის გამოყენებით, ეს ოპერაციები ბევრად უფრო სწრაფია, ვიდრე SyncLock ბრძანების გამოყენება.

გადაკეტვა

სინქრონიზაციის დროს, საკეტი დაყენებულია ობიექტებზე და არა ძაფებზე, ამიტომ გამოყენებისას განსხვავებულიდაბლოკვის ობიექტები განსხვავებულიპროგრამებში კოდის ფრაგმენტები ზოგჯერ საკმაოდ არასერიოზული შეცდომები ხდება. სამწუხაროდ, ხშირ შემთხვევაში ერთ ობიექტზე სინქრონიზაცია უბრალოდ მიუღებელია, რადგან ეს გამოიწვევს ძაფების ძალიან ხშირად დაბლოკვას.

განვიხილოთ სიტუაცია გადაჯაჭვული(ჩიხი) მისი უმარტივესი ფორმით. წარმოიდგინეთ ორი პროგრამისტი სადილის მაგიდასთან. სამწუხაროდ, მათ აქვთ მხოლოდ ერთი დანა და ერთი ჩანგალი ორზე. დავუშვათ, რომ საჭმელად გჭირდებათ დანა და ჩანგალი, შესაძლებელია ორი სიტუაცია:

  • ერთი პროგრამისტი ახერხებს დანა და ჩანგლის დაჭერას და ჭამას იწყებს. როდესაც ის სავსეა, ის დებს სადილს, შემდეგ კი სხვა პროგრამისტს შეუძლია წაიღოს ისინი.
  • ერთი პროგრამისტი იღებს დანას და მეორე იღებს ჩანგალს. არცერთს არ შეუძლია დაიწყოს ჭამა, თუ მეორე არ დაანებებს თავის მოწყობილობას.

მრავალსიდიანი პროგრამაში ამ სიტუაციას ეწოდება ურთიერთბლოკირება.ორი მეთოდი სინქრონიზებულია სხვადასხვა ობიექტზე. თემა A იღებს ობიექტს 1 და შედის ამ ობიექტის მიერ დაცულ პროგრამის ნაწილში. სამწუხაროდ, იმისათვის, რომ იმუშაოს, მას სჭირდება წვდომა სხვა Sync Lock- ით დაცულ კოდზე განსხვავებული სინქრონიზაციის ობიექტით. მაგრამ სანამ დრო ექნება შევიდეს ფრაგმენტი, რომელიც სინქრონიზებულია სხვა ობიექტის მიერ, ნაკადი B შედის მასში და იჭერს ამ ობიექტს. ახლა ძაფი A ვერ შევა მეორე ფრაგმენტში, ძაფი B ვერ შევა პირველ ფრაგმენტში და ორივე ძაფი განწირულია ლოდინით უსასრულოდ. ვერანაირი ძაფი ვერ გააგრძელებს გაშვებას, რადგანაც საჭირო ობიექტი არასოდეს გათავისუფლდება.

ჩიხების დიაგნოზი გართულებულია იმით, რომ ისინი შეიძლება მოხდეს შედარებით იშვიათ შემთხვევებში. ეს ყველაფერი დამოკიდებულია იმაზე, თუ რა თანმიმდევრობით გამოყოფს გრაფიკი მათ CPU– ს. შესაძლებელია, რომ უმეტეს შემთხვევაში, სინქრონიზაციის ობიექტები დაიჭირეს ჩიხიანი წესით.

ქვემოთ მოცემულია ახლანდელი აღწერილი ჩიხიანი სიტუაციის განხორციელება. ყველაზე ფუნდამენტური პუნქტების მოკლე განხილვის შემდეგ, ჩვენ ვაჩვენებთ როგორ გამოვყოთ ჩიხიანი სიტუაცია ძაფის ფანჯარაში:

1 ვარიანტი მკაცრად ჩართულია

2 იმპორტის სისტემა. თემა

3 მოდულის მოდული

4 ქვე ძირითადი ()

5 Dim Tom როგორც ახალი პროგრამისტი ("ტომ")

6 დიმ ბობ როგორც ახალი პროგრამისტი ("ბობ")

7 Dim aThreadStart როგორც ახალი ThreadStart (მისამართი Tom.Eat)

8 Dim aThread როგორც ახალი თემა (aThreadStart)

9 aThread.Name = "ტომ"

10 Dim bThreadStart As New ThreadStarttAddressOf Bob.Eat)

11 Dim bThread როგორც ახალი თემა (bThreadStart)

12 bThread.Name = "ბობ"

13 aThread.Start ()

14 ბ თემა. დაწყება ()

15 ბოლო ქვე

16 ბოლო მოდული

17 საჯარო კლასის ჩანგალი

18 პირადი გაზიარებული mForkAvaiTable როგორც ლოგიკური = მართალია

19 პირადი გაზიარებული მფლობელი როგორც სიმებიანი = "არავინ"

20 პირადი კითხულობს მხოლოდ საკუთრების OwnsUtensil () როგორც სიმებიანი

21 მიიღეთ

22 დაბრუნება მფლობელი

23 დასასრულის მიღება

24 ბოლო საკუთრება

25 Public Sub GrabForktByVal a როგორც პროგრამისტი)

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

"ცდილობს ჩანგლის დაჭერას.")

27 Console.WriteLine (Me.OwnsUtensil & "აქვს ჩანგალი."). ...

28 მონიტორი. შეიყვანეთ (მე) "SyncLock (aFork)"

29 თუ mFork ხელმისაწვდომია მაშინ

30 ა. HasFork = მართალია

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 პირადი გაზიარებული მფლობელი როგორც სიმებიანი = "არავინ"

42 პირადი კითხულობს მხოლოდ საკუთრებას OwnsUtensi1 () როგორც სიმებიანი

43 მიიღეთ

44 დაბრუნება მფლობელი

45 დასრულება მიიღეთ

46 ბოლო საკუთრება

47 Public Sub GrabKnifetByVal a როგორც პროგრამისტი)

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

"ცდილობს დანა აიღოს.")

49 Console.WriteLine (Me.OwnsUtensil & "აქვს დანა.")

50 მონიტორი. შეიყვანეთ (მე) "SyncLock (დანა)"

51 თუ mKnife ხელმისაწვდომია მაშინ

52 mKnifeAvailable = ყალბი

53 a.HasKnife = მართალია

54 mOwner = a.MyName

55 Console.WriteLine (a.MyName & "ახლახანს დანა მივიღე. ლოდინი")

56 სცადე

თემა. ძილი (100)

დაიჭირე e როგორც გამონაკლისი

Console.WriteLine (e.StackTrace)

დაასრულე ცდა

57 დასასრულს თუ

58 მონიტორი. გასვლა (მე)

59 დასასრულის ქვე

60 ბოლო კლასი

61 საჯარო კლასის პროგრამისტი

62 პირადი mName As String

63 პირადი გაზიარებული mFork როგორც ჩანგალი

64 პირადი გაზიარებული mKnife As Knife

65 პირადი mHasKnife As Boolean

66 პირადი mHasFork As Boolean

67 გაზიარებული ქვე -ახალი ()

68 mFork = ახალი ჩანგალი ()

69 mKnife = ახალი დანა ()

70 ბოლო ქვე

71 Public Sub New (ByVal theName As String)

72 მ სახელი = სახელი

73 დასასრულის ქვე

74 საჯარო მხოლოდ წაკითხული საკუთრება MyName () როგორც სიმებიანი

75 მიიღეთ

76 დაბრუნება mName

77 დასასრულის მიღება

78 ბოლო საკუთრება

79 საზოგადოებრივი საკუთრება HasKnife () როგორც ლოგიკური

80 მიიღეთ

81 დაბრუნება mHasKnife

82 დასასრულის მიღება

83 კომპლექტი (ByVal Value As Boolean)

84 mHasKnife = მნიშვნელობა

85 ბოლო კომპლექტი

86 ბოლო ქონება

87 საზოგადოებრივი საკუთრება HasFork () როგორც ლოგიკური

88 მიიღეთ

89 დაბრუნება mHasFork

90 დასასრულის მიღება

91 კომპლექტი (ByVal Value As Boolean)

92 mHasFork = მნიშვნელობა

93 ბოლო კომპლექტი

94 ბოლო საკუთრება

95 საჯარო ქვე ჭამა ()

96 Do Before Me.HasKnife And Me.HasFork

97 Console.Writeline (Thread.CurrentThread.Name & "არის თემაში.")

98 თუ რნდ ()< 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) ქმნის პროგრამისტების ორ მაგალითს და შემდეგ იწყებს ორ ძაფს პროგრამისტების კლასის კრიტიკული Eat მეთოდის შესასრულებლად (სტრიქონები 95-108), ქვემოთ აღწერილი. მთავარი პროცედურა ადგენს ძაფების სახელებს და ადგენს მათ; ალბათ ყველაფერი რაც ხდება გასაგებია და კომენტარის გარეშე.

Fork კლასის კოდი გამოიყურება უფრო საინტერესო (სტრიქონები 17-38) (მსგავსი Knife კლასი განისაზღვრება 39-60 სტრიქონებში). მე -18 და მე -19 სტრიქონებში მითითებულია საერთო ველების მნიშვნელობები, რომლითაც შეგიძლიათ გაარკვიოთ არის თუ არა დანამატი ამჟამად და თუ არა, ვინ იყენებს მას. ReadOnly საკუთრება OwnUtensi1 (სტრიქონები 20-24) განკუთვნილია ინფორმაციის უმარტივესი გადასაცემად. ჩანგლის კლასში არის GrabFork "აიღე ჩანგალი" მეთოდი, რომელიც განსაზღვრულია 25-27 სტრიქონებში.

  1. 26 და 27 სტრიქონები უბრალოდ ბეჭდავს გამართვის ინფორმაციას კონსოლზე. მეთოდის მთავარ კოდში (სტრიქონები 28-36), ჩანგალზე წვდომა სინქრონიზებულია ობიექტებითქამარი მე. ვინაიდან ჩვენი პროგრამა იყენებს მხოლოდ ერთ ჩანგალს, Me sync უზრუნველყოფს იმას, რომ ორი ძაფის დაჭერა არ შეუძლია ერთდროულად. Slee "p ბრძანება (ბლოკში, რომელიც იწყება 34 -ე სტრიქონიდან) ახდენს სიმულაციის შეფერხებას ჩანგლის / დანის ჭერასა და ჭამის დაწყებას შორის. გაითვალისწინეთ, რომ Sleep ბრძანება არ ხსნის საგნებს და მხოლოდ აჩქარებს ჩიხებს!
    თუმცა, ყველაზე საინტერესო არის პროგრამისტის კლასის კოდი (სტრიქონები 61-108). 67-70 ხაზები განსაზღვრავს ზოგად კონსტრუქტორს იმის უზრუნველსაყოფად, რომ პროგრამაში არის მხოლოდ ერთი ჩანგალი და დანა. საკუთრების კოდი (სტრიქონები 74-94) მარტივია და კომენტარს არ საჭიროებს. ყველაზე მნიშვნელოვანი ხდება Eat მეთოდით, რომელიც შესრულებულია ორი ცალკეული ძაფით. პროცესი გრძელდება მარყუჟში, სანამ ნაკადი არ დაიჭერს ჩანგალს დანასთან ერთად. 98-102 ხაზებზე, ობიექტი შემთხვევით იჭერს ჩანგალს / დანა Rnd ზარის გამოყენებით, რაც იწვევს ჩიხს. ხდება შემდეგი:
    ძაფი, რომელიც ასრულებს Thoth ობიექტის Eat მეთოდს, იძახება და შედის მარყუჟში. ის იჭერს დანა და გადადის ლოდინის მდგომარეობაში.
  2. ძაფი, რომელიც ასრულებს Bob's Eat მეთოდს, გამოიძახება და შემოდის მარყუჟში. მას არ შეუძლია დანა აიღოს, მაგრამ აიღებს ჩანგალს და გადადის ლოდინის მდგომარეობაში.
  3. ძაფი, რომელიც ასრულებს Thoth ობიექტის Eat მეთოდს, იძახება და შედის მარყუჟში. ის ცდილობს ჩანგლის დაჭერას, მაგრამ ბობმა უკვე დაიჭირა ჩანგალი; ძაფი გადადის ლოდინის მდგომარეობაში.
  4. ძაფი, რომელიც ასრულებს Bob's Eat მეთოდს, გამოიძახება და შემოდის მარყუჟში. ის ცდილობს დანა აიღოს, მაგრამ დანა უკვე შეპყრობილია ობიექტის თოთის მიერ; ძაფი გადადის ლოდინის მდგომარეობაში.

ეს ყველაფერი გრძელდება განუსაზღვრელი ვადით - ჩვენ ვდგავართ ჩიხის ტიპიური სიტუაციის წინაშე (სცადეთ პროგრამის გაშვება და ნახავთ, რომ არავის შეუძლია ამ გზით ჭამა).
თქვენ ასევე შეგიძლიათ შეამოწმოთ მოხდა თუ არა ჩიხი ძაფების ფანჯარაში. გაუშვით პროგრამა და გააწყვეტინეთ Ctrl + Break ღილაკებით. ჩართეთ Me ცვლადი ხედის ფანჯარაში და გახსენით ნაკადების ფანჯარა. შედეგი ჰგავს იმას, რაც ნაჩვენებია ნახატზე. 10.7. ფიგურადან ხედავთ, რომ ბობის ძაფს დანა აქვს დაჭერილი, მაგრამ მას ჩანგალი არ აქვს. დააწკაპუნეთ მარჯვენა ღილაკით თემების ფანჯარაში Tot ხაზზე და კონტექსტური მენიუდან აირჩიეთ ბრძანება Switch to Thread. ხედი აჩვენებს, რომ თოთის ნაკადს აქვს ჩანგალი, მაგრამ დანა არა. რასაკვირველია, ეს არ არის ასი პროცენტიანი მტკიცებულება, მაგრამ ასეთი საქციელი მაინც გეპარებათ ეჭვი, რომ რაღაც არასწორია.
თუ ერთი ობიექტის სინქრონიზაციით ვარიანტი (როგორც პროგრამაში -ტემპერატურის გაზრდა სახლში) შეუძლებელია, ორმხრივი დაბლოკვის თავიდან ასაცილებლად შეგიძლიათ დაითვალოთ სინქრონიზაციის ობიექტები და ყოველთვის დაიჭიროთ ისინი მუდმივი თანმიმდევრობით. მოდით გავაგრძელოთ სასადილო პროგრამისტის ანალოგია: თუ ძაფი ყოველთვის იღებს დანა ჯერ და შემდეგ ჩანგალს, ჩიხთან დაკავშირებული პრობლემები არ იქნება. პირველი ნაკადი, რომელიც დანას იჭერს, შეძლებს ნორმალურად ჭამას. პროგრამის ნაკადების ენაზე თარგმნილი, ეს ნიშნავს, რომ ობიექტის 2 დაჭერა შესაძლებელია მხოლოდ იმ შემთხვევაში, თუ ობიექტი 1 პირველად დაიჭირეს.

ბრინჯი 10.7. ჩიხების ანალიზი ძაფის ფანჯარაში

ამიტომ, თუ ჩვენ ამოვიღებთ ზარს Rnd 98 -ე ხაზზე და შევცვლით მას ამონარიდით

mFork.GrabFork (მე)

mKnife.GrabKnife (მე)

ჩიხი ქრება!

ითანამშრომლეთ მონაცემების შექმნისას

მრავალსართულიან აპლიკაციებში ხშირად არის სიტუაცია, როდესაც ძაფები არა მხოლოდ მუშაობენ გაზიარებულ მონაცემებთან, არამედ დაელოდებიან მის გამოჩენას (ანუ ძაფი 1 -მა უნდა შექმნა მონაცემები სანამ ძაფი 2 გამოიყენებს მას). მას შემდეგ, რაც მონაცემები გაზიარებულია, მასზე წვდომა უნდა იყოს სინქრონიზებული. ასევე აუცილებელია უზრუნველყოს საშუალებები მზა მონაცემების გამოჩენის შესახებ ელოდება ძაფებს.

ამ სიტუაციას ჩვეულებრივ უწოდებენ მიმწოდებლის / მომხმარებლის პრობლემა.ძაფი ცდილობს წვდომა იქამდე არსებულ მონაცემებზე, ამიტომ უნდა გადასცეს კონტროლი სხვა ძაფზე, რომელიც ქმნის საჭირო მონაცემებს. პრობლემა მოგვარებულია შემდეგი კოდით:

  • თემა 1 (მომხმარებელი) იღვიძებს, შედის სინქრონიზებული მეთოდი, ეძებს მონაცემებს, არ პოულობს მას და გადადის ლოდინის მდგომარეობაში. წინასწარფიზიკურად, მან უნდა ამოიღოს ბლოკირება ისე, რომ ხელი არ შეუშალოს მიმწოდებელი ძაფის მუშაობას.
  • თემა 2 (პროვაიდერი) შედის სინქრონიზებული მეთოდი, რომელიც გათავისუფლებულია ძაფით 1, ქმნისნაკადი 1 -ისთვის და რატომღაც აცნობებს ნაკადს 1 მონაცემების არსებობის შესახებ. შემდეგ ის ათავისუფლებს საკეტს ისე, რომ ძაფს 1 შეუძლია ახალი მონაცემების დამუშავება.

ნუ ეცდებით ამ პრობლემის მოგვარებას თემა 1 -ის გამუდმებით მოწოდებით და მდგომარეობის ცვლადის მდგომარეობის შემოწმებით, რომლის მნიშვნელობა>> არის ძაფი 2. ეს გადაწყვეტილება სერიოზულად იმოქმედებს თქვენი პროგრამის მუშაობაზე, რადგან უმეტეს შემთხვევაში 1 თემა გამოძახება უმიზეზოდ; და თემა 2 დაელოდება იმდენად ხშირად, რომ მონაცემების შესაქმნელად დრო ამოიწურება.

პროვაიდერ / მომხმარებელთან ურთიერთობა ძალიან ხშირია, ამიტომ სპეციალური პრიმიტივები იქმნება ამგვარი სიტუაციებისთვის მრავალპროფილიანი პროგრამირების კლასის ბიბლიოთეკებში. NET– ში ამ პრიმიტივებს ეწოდება Wait და Pulse-PulseAl 1 და მონიტორის კლასის ნაწილია. სურათი 10.8 ასახავს სიტუაციას, რომლის დაპროგრამებასაც ვაპირებთ. პროგრამა აწყობს სამი ძაფის რიგს: ლოდინის რიგს, ბლოკირების რიგს და შესრულების რიგს. ძაფების განმსაზღვრელი არ გამოყოფს CPU დროს ძაფებს, რომლებიც ლოდინის რიგშია. ძაფს რომ დრო დაეთმოს, ის უნდა გადავიდეს შესრულების რიგში. შედეგად, აპლიკაციის მუშაობა ორგანიზებულია ბევრად უფრო ეფექტურად, ვიდრე პირობითი ცვლადის ჩვეულებრივი გამოკითხვა.

ფსევდოკოდში, მონაცემთა სამომხმარებლო იდიომი ჩამოყალიბებულია შემდეგნაირად:

"შესვლა შემდეგი ტიპის სინქრონიზებულ ბლოკში

მიუხედავად იმისა, რომ მონაცემები არ არის

გადადით ლოდინის რიგში

მარყუჟი

თუ არსებობს მონაცემები, დაამუშავეთ იგი.

დატოვეთ სინქრონიზებული ბლოკი

Wait ბრძანების შესრულებისთანავე, ძაფი შეჩერებულია, საკეტი იხსნება და ძაფი შედის ლოდინის რიგში. როდესაც საკეტი გათავისუფლდება, შესრულების რიგში ძაფის გაშვება ნებადართულია. დროთა განმავლობაში, ერთი ან მეტი დაბლოკილი ძაფი შექმნის მონაცემებს, რომლებიც აუცილებელია ძაფის მუშაობისთვის, რომელიც ელოდება რიგში. მას შემდეგ, რაც მონაცემთა ვალიდაცია ხორციელდება მარყუჟში, მონაცემთა გამოყენებაზე გადასვლა (მარყუჟის შემდეგ) ხდება მხოლოდ მაშინ, როდესაც დამუშავებისათვის მზად არის მონაცემები.

ფსევდოკოდში, მონაცემთა მიმწოდებლის იდიომი ასე გამოიყურება:

"სინქრონიზებული ხედის ბლოკში შესვლა

მიუხედავად იმისა, რომ მონაცემები არ არის საჭირო

გადადით ლოდინის რიგში

სხვა აწარმოოს მონაცემები

როდესაც მონაცემები მზად არის, დარეკეთ Pulse-PulseAll.

ერთი ან მეტი ძაფის გადატანა ბლოკირების რიგიდან შესრულების რიგში. დატოვეთ სინქრონიზებული ბლოკი (და დაუბრუნდით გაშვების რიგში)

დავუშვათ, ჩვენი პროგრამა ასახავს ოჯახს ერთი მშობლით, რომელიც ფულს აკეთებს და ბავშვი, რომელიც ხარჯავს ამ ფულს. როდესაც ფული დასრულდებაგამოდის, რომ ბავშვმა უნდა დაელოდოს ახალი თანხის ჩამოსვლას. ამ მოდელის პროგრამული უზრუნველყოფა ასე გამოიყურება:

1 ვარიანტი მკაცრად ჩართულია

2 იმპორტის სისტემა. თემა

3 მოდულის მოდული

4 ქვე ძირითადი ()

5 Dim theFamily როგორც ახალი ოჯახი ()

6 theFamily.StartltsLife ()

7 დასასრული ქვე

8 ფიოდულის დასასრული

9

10 საჯარო კლასის ოჯახი

11 პირადი mMoney As Integer

12 პირადი mWeek როგორც მთელი რიცხვი = 1

13 Public Sub StartltsLife ()

14 Dim aThreadStart As New ThreadStarUAddressOf.Produce)

15 Dim bThreadStart As New ThreadStarUAddressOf.Consume)

16 Dim aThread როგორც ახალი თემა (aThreadStart)

17 Dim bThread როგორც ახალი თემა (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 მე. ჩვენი ფული = 1000

47 მონიტორი. პულსი ყველა (მე)

48 მონიტორი. გასვლა (მე)

49 მარყუჟი

50 დასასრულის ქვე

51 საჯარო ქვემომხმარებელი ()

52 MsgBox ("მოხმარების თემაში ვარ")

53 გააკეთე

54 მონიტორი. შედი (მე)

55 Do while Me.OurMoney = 0

56 მონიტორი. დაელოდე (მე)

57 მარყუჟი

58 Console.WriteLine ("ძვირფასო მშობლებო, მე უბრალოდ დავხარჯე თქვენი ყველა" & _

ფული კვირაში "და კვირა)

59 კვირა + = 1

60 თუ კვირა = 21 * 52 მაშინ სისტემა. გარემო. გასვლა (0)

61 მე. ჩვენი ფული = 0

62 მონიტორი. პულსი ყველა (მე)

63 მონიტორი. გასვლა (მე)

64 მარყუჟი

65 დასასრულის ქვე

66 ბოლო კლასი

StartltsLife მეთოდი (სტრიქონები 13-22) ემზადება წარმოებისა და მოხმარების ნაკადების დასაწყებად. ყველაზე მნიშვნელოვანი ხდება წარმოების (ხაზები 39-50) და მოხმარების (სტრიქონები 51-65) ნაკადებში. Sub Produce პროცედურა ამოწმებს ფულის ხელმისაწვდომობას და თუ არის ფული, ის გადადის ლოდინის რიგში. წინააღმდეგ შემთხვევაში, მშობელი გამოიმუშავებს ფულს (სტრიქონი 46) და აცნობს ობიექტებს მოსაცდელ რიგში სიტუაციის შეცვლის შესახებ. გაითვალისწინეთ, რომ ზარი Pulse-Pulse All ძალაში შედის მხოლოდ მაშინ, როდესაც საკეტი გათავისუფლდება Monitor.Exit ბრძანებით. პირიქით, Sub Consume პროცედურა ამოწმებს ფულის ხელმისაწვდომობას და თუ ფული არ არის, ამის შესახებ აცნობებს მოლოდინ მშობელს. ხაზი 60 უბრალოდ წყვეტს პროგრამას 21 პირობითი წლის შემდეგ; დარეკვის სისტემა. გარემო. გამოსვლა (0) არის End ბრძანების. NET ანალოგი (End ბრძანება ასევე მხარდაჭერილია, მაგრამ სისტემისგან განსხვავებით. გარემოს. გასვლა, ის არ აბრუნებს ოპერაციულ სისტემას გასასვლელ კოდს).

თემები, რომლებიც მოთავსებულია ლოდინის რიგში, უნდა გათავისუფლდეს თქვენი პროგრამის სხვა ნაწილების მიერ. სწორედ ამ მიზეზით გვირჩევნია PulseAll პულსის გამოყენება. ვინაიდან წინასწარ არ არის ცნობილი რომელი ძაფი გააქტიურდება, როდესაც პულსი 1 იძახება, ძაფების შედარებით მცირე რაოდენობის რიგში, თქვენ ასევე შეგიძლიათ დარეკოთ PulseAll.

გრაფიკული პროგრამების მრავალმხრივი კითხვა

GUI პროგრამებში მულტიტრედის ჩვენი განხილვა იწყება მაგალითით, რომელიც განმარტავს, რისთვის არის განკუთვნილი GUI პროგრამებში მულტიტრედინგი. შექმენით ფორმა ორი ღილაკით Start (btnStart) და Cancel (btnCancel), როგორც ეს ნაჩვენებია ნახ. 10.9. დაწყების ღილაკზე დაჭერით წარმოიქმნება კლასი, რომელიც შეიცავს შემთხვევით სტრიქონს 10 მილიონი სიმბოლოთი და მეთოდი ამ გრძელი სტრიქონის ასო "E" - ს გამოსათვლელად. გაითვალისწინეთ StringBuilder კლასის გამოყენება გრძელი სტრიქონების უფრო ეფექტური შექმნისათვის.

Ნაბიჯი 1

თემა 1 შენიშნავს, რომ არ არსებობს მონაცემები ამის შესახებ. ის ურეკავს Wait, ათავისუფლებს საკეტს და მიდის ლოდინის რიგში.



ნაბიჯი 2

როდესაც საკეტი იხსნება, ძაფი 2 ან ძაფი 3 ტოვებს ბლოკის რიგს და შედის სინქრონიზებულ ბლოკში, იძენს საკეტს

ნაბიჯი 3

ვთქვათ, თემა 3 შედის სინქრონიზებულ ბლოკში, ქმნის მონაცემებს და ეძახის Pulse-Pulse All.

ბლოკიდან გასვლის და საკეტის გათავისუფლებისთანავე, ძაფი 1 გადადის შესრულების რიგში. თუ ძაფი 3 მოუწოდებს Pluse- ს, მხოლოდ ერთი შედის შესრულების რიგშითემა, როდესაც Pluse All ეწოდება, ყველა თემა გადადის შესრულების რიგში.



ბრინჯი 10.8. მომწოდებლის / მომხმარებლის პრობლემა

ბრინჯი 10.9. Multithreading მარტივი GUI პროგრამაში

იმპორტის სისტემა. ტექსტი

საჯარო კლასის შემთხვევითი პერსონაჟები

პირადი m_Data როგორც StringBuilder

პირადი mjength, m_count როგორც მთელი რიცხვი

Public Sub New (ByVal n როგორც მთელი რიცხვი)

m_ სიგრძე = n -1

m_Data = ახალი StringBuilder (m_length) MakeString ()

დასასრული ქვე

პირადი ქვე MakeString ()

Dim i როგორც მთელი რიცხვი

Dim myRnd როგორც ახალი შემთხვევითი ()

I = 0 -დან m_length- მდე

"შექმენით შემთხვევითი რიცხვი 65 -დან 90 -მდე,

"გადააქციე დიდზე

"და მიამაგრეთ StringBuilder ობიექტს

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

შემდეგი

დასასრული ქვე

საჯარო ქვე StartCount ()

GetEes ()

დასასრული ქვე

პირადი ქვე GetEes ()

Dim i როგორც მთელი რიცხვი

I = 0 -დან m_length- მდე

თუ m_Data.Chars (i) = CChar ("E") მაშინ

m_count + = 1

დასრულება თუ შემდეგი

m_CountDone = მართალია

დასასრული ქვე

საჯაროდ მხოლოდ კითხულობს

ქონების 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 როგორც ახალი შემთხვევითი პერსონაჟი (10000000)

RC.StartCount ()

MsgBox ("es- ების რაოდენობა არის" & RC.GetCount)

დასასრული ქვე

გაუქმების ღილაკი აჩვენებს შეტყობინების ყუთს:

პირადი ქვე btnCancel_Click (ByVal გამგზავნი როგორც System.Object._

ByVal e As System.EventArgs) სახელურები btnCancel.Click

MsgBox ("რაოდენობა შეწყდა!")

დასასრული ქვე

პროგრამის გაშვებისას და დაწყების ღილაკზე დაჭერისას აღმოჩნდება, რომ გაუქმების ღილაკი არ პასუხობს მომხმარებლის შეყვანას, რადგან უწყვეტი მარყუჟი ხელს უშლის ღილაკს იმ მოვლენის მართვაში, რომელსაც იღებს. ეს მიუღებელია თანამედროვე პროგრამებში!

არსებობს ორი შესაძლო გამოსავალი. პირველი ვარიანტი, რომელიც კარგად არის ცნობილი წინა VB ვერსიებიდან, არ ითვალისწინებს მულტიტრედინს: DoEvents ზარი შედის მარყუჟში. NET– ში ეს ბრძანება ასე გამოიყურება:

განაცხადი. DoEvents ()

ჩვენს მაგალითში, ეს ნამდვილად არ არის სასურველი - ვის უნდა შეანელოს პროგრამა ათი მილიონი DoEvents ზარით! თუ თქვენ ნაცვლად გამოყოფთ მარყუჟს ცალკეულ ძაფზე, ოპერაციული სისტემა გადადის ძაფებს შორის და ღილაკი გაუქმება ფუნქციონალური დარჩება. განხორციელება ცალკეული ძაფით ნაჩვენებია ქვემოთ. იმის მკაფიოდ დასანახად, რომ გაუქმების ღილაკი მუშაობს, როდესაც ჩვენ ვაწკაპუნებთ, ჩვენ უბრალოდ ვწყვეტთ პროგრამას.

შემდეგი ნაბიჯი: ღილაკის ჩვენება რაოდენობა

ვთქვათ, თქვენ გადაწყვიტეთ აჩვენოთ თქვენი შემოქმედებითი წარმოსახვა და ფორმას მიანიჭოთ სურათი, რომელიც ნაჩვენებია ლეღვში. 10.9. გთხოვთ გაითვალისწინოთ: ღილაკის ჩვენების რაოდენობა ჯერ არ არის ხელმისაწვდომი.

ბრინჯი 10.10. ჩაკეტილი ღილაკის ფორმა

ცალკე თემა გვთავაზობს დათვლას და განბლოკავს მიუწვდომელ ღილაკს. ეს, რა თქმა უნდა, შეიძლება გაკეთდეს; უფრო მეტიც, ასეთი ამოცანა საკმაოდ ხშირად ჩნდება. სამწუხაროდ, თქვენ ვერ შეძლებთ იმოქმედოთ ყველაზე აშკარა გზით - დააკავშიროთ მეორადი თემა GUI ძაფს კონსტრუქტორში ShowCount ღილაკზე ბმულის შენარჩუნებით, ან თუნდაც სტანდარტული დელეგატის გამოყენებით. Სხვა სიტყვებით, არასოდესარ გამოიყენოთ ქვემოთ მოცემული ვარიანტი (ძირითადი მცდარიხაზები თამამია).

საჯარო კლასის შემთხვევითი პერსონაჟები

პირადი m_0ata როგორც StringBuilder

პირადი m_CountDone როგორც ლოგიკური

პირადი მეგენტე. m_count როგორც მთელი რიცხვი

პირადი m_Button როგორც Windows.Forms.Button

Public Sub New (ByVa1 n როგორც მთელი რიცხვი, _

ByVal b როგორც Windows.Forms.Button)

m_length = n - 1

m_Data = ახალი StringBuilder (mJength)

m_Button = b MakeString ()

დასასრული ქვე

პირადი ქვე MakeString ()

Dim I როგორც მთელი რიცხვი

Dim myRnd როგორც ახალი შემთხვევითი ()

I = 0 -დან m_length- მდე

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

შემდეგი

დასასრული ქვე

საჯარო ქვე StartCount ()

GetEes ()

დასასრული ქვე

პირადი ქვე GetEes ()

Dim I როგორც მთელი რიცხვი

I = 0 to mjength

თუ m_Data.Chars (I) = CChar ("E") მაშინ

m_count + = 1

დასრულება თუ შემდეგი

m_CountDone = მართალია

m_Button.Enabled = მართალია

დასასრული ქვე

საჯაროდ მხოლოდ კითხულობს

ქონების GetCount () როგორც მთელი რიცხვი

მიიღეთ

თუ არა (m_CountDone) მაშინ

ჩააგდე ახალი გამონაკლისი ("დათვლა ჯერ არ დასრულებულა") სხვა

დაბრუნება m_count

Დაასრულე თუ

დასრულება მიიღეთ

თვისების დასრულება

საჯარო მხოლოდ წაკითხული საკუთრება IsDone () როგორც ლოგიკური

მიიღეთ

დაბრუნება m_CountDone

დასრულება მიიღეთ

თვისების დასრულება

დასასრულის კლასი

სავარაუდოა, რომ ეს კოდი იმუშავებს ზოგიერთ შემთხვევაში. მიუხედავად ამისა:

  • მეორადი ძაფის ურთიერთქმედება ძაფთან GUI– ს შექმნით შეუძლებელია ორგანიზება აშკარანიშნავს.
  • არასოდესარ შეცვალოთ გრაფიკული პროგრამების ელემენტები სხვა პროგრამის ნაკადებიდან. ყველა ცვლილება უნდა მოხდეს მხოლოდ იმ თემაში, რომელმაც შექმნა GUI.

თუ თქვენ დაარღვევთ ამ წესებს, ჩვენ ჩვენ გარანტიას ვაძლევთრომ დახვეწილი, დახვეწილი შეცდომები მოხდება თქვენს მრავალ ხრახნიან გრაფიკულ პროგრამებში.

ის ასევე ვერ მოახერხებს მოვლენების გამოყენებით ობიექტების ურთიერთქმედების ორგანიზებას. 06 ღონისძიების თანამშრომელი მუშაობს იმავე ძაფზე, რომელსაც RaiseEvent ერქვა, ასე რომ მოვლენები არ დაგეხმარება.

და მაინც, საღი აზრი გვკარნახობს, რომ გრაფიკულ პროგრამებს უნდა ჰქონდეთ სხვა ძაფის ელემენტების შეცვლის საშუალება. NET Framework– ში არსებობს ძაფის უსაფრთხო გზა GUI პროგრამების მეთოდების სხვა ძაფის გამოძახების მიზნით. მეთოდის სპეციალური ტიპი Invoker დელეგატი სისტემიდან. Windows სახელების სივრცე გამოიყენება ამ მიზნით. ფორმები. შემდეგი ფრაგმენტი აჩვენებს GetEes მეთოდის ახალ ვერსიას (შეიცვალა ხაზები თამამად):

პირადი ქვე GetEes ()

Dim I როგორც მთელი რიცხვი

I = 0 -დან m_length- მდე

თუ m_Data.Chars (I) = CChar ("E") მაშინ

m_count + = 1

დასრულება თუ შემდეგი

m_CountDone = ჭეშმარიტი ცდა

Dim mylnvoker როგორც ახალი მეთოდიlnvoker (AddressOf UpDateButton)

myInvoker.Invoke () Catch e As ThreadlnterruptException

"წარუმატებლობა

დაასრულე ცდა

დასასრული ქვე

საჯარო ქვე UpDateButton ()

m_Button.Enabled = მართალია

დასასრული ქვე

ღილაკზე ძაფთაშორისი ზარები ხდება არა უშუალოდ, არამედ Method Invoker– ის საშუალებით. .NET Framework გარანტიას იძლევა, რომ ეს ვარიანტი ძაფის უსაფრთხოა.

რატომ არის ამდენი პრობლემა მრავალძირიან პროგრამირებასთან დაკავშირებით?

ახლა, როდესაც თქვენ გაქვთ გარკვეული გაგება მრავალსიტყვიანი ტექსტისა და მასთან დაკავშირებული პოტენციური პრობლემების შესახებ, ჩვენ გადავწყვიტეთ, რომ მიზანშეწონილი იქნებოდა ამ ქვეთავის სათაურის კითხვაზე პასუხის გაცემა ამ თავის ბოლოს.

ერთ-ერთი მიზეზი ის არის, რომ მრავალსიდიანი ტექსტი არაწრფივი პროცესია და ჩვენ შეჩვეულები ვართ წრფივი პროგრამირების მოდელს. თავდაპირველად, ძნელია შეეგუო იმ აზრს, რომ პროგრამის შესრულება შეიძლება შემთხვევით შეწყდეს და კონტროლი გადაეცემა სხვა კოდს.

თუმცა, არის კიდევ ერთი, უფრო ფუნდამენტური მიზეზი: ამ დღეებში პროგრამისტები ძალიან იშვიათად ასამბლერში პროგრამირებენ, ან სულ მცირე, უყურებენ შემდგენლის დაშლილ გამოსავალს. წინააღმდეგ შემთხვევაში, მათთვის ბევრად უფრო ადვილი იქნება შეეგუონ იმ აზრს, რომ ასობით ასამბლეის ინსტრუქცია შეიძლება შეესაბამებოდეს მაღალი დონის ერთ ბრძანებას (მაგალითად VB .NET). ძაფი შეიძლება შეწყდეს რომელიმე ამ ინსტრუქციის შემდეგ და, შესაბამისად, მაღალი დონის ბრძანების შუაგულში.

მაგრამ ეს არ არის ყველაფერი: თანამედროვე შემდგენლები ოპტიმიზირებენ პროგრამის მუშაობას და კომპიუტერულ ტექნიკას შეუძლია ხელი შეუშალოს მეხსიერების მართვას. შედეგად, შემდგენელს ან ტექნიკას შეუძლია შეცვალოს პროგრამის წყაროს კოდში მითითებული ბრძანებების თანმიმდევრობა თქვენი ცოდნის გარეშე [ ბევრი შემდგენელი ოპტიმიზირებს მასივების ციკლურ კოპირებას, როგორიცაა i = 0 to n: b (i) = a (i): ncxt. შემდგენელს (ან თუნდაც სპეციალიზებულ მეხსიერების მენეჯერს) შეუძლია უბრალოდ შექმნას მასივი და შემდეგ შეავსოს იგი ერთი ასლის ოპერაციით, ნაცვლად იმისა, რომ ცალკეული ელემენტები მრავალჯერ გადაწერა!].

ვიმედოვნებთ, რომ ეს ახსნა დაგეხმარებათ უკეთ გაიგოთ, თუ რატომ იწვევს მრავალმხრივი პროგრამირება ამდენ პრობლემას - ან არანაკლებ გაგიკვირდებათ თქვენი მრავალსიდიანი პროგრამების უცნაური ქცევა!

მაგალითი იმისა, თუ როგორ უნდა ავაშენოთ მარტივი მრავალფუნქციური პროგრამა.

დაიბადა მრავალი კითხვის გამო დელფში მრავალსართულიანი პროგრამების შექმნის შესახებ.

ამ მაგალითის მიზანია იმის დემონსტრირება, თუ როგორ სწორად ავაშენოთ მრავალსართულიანი პროგრამა, ცალკეულ ძაფში გრძელვადიანი მუშაობის ამოღებით. და როგორ, ასეთ განაცხადში, უზრუნველყოს ძირითადი ძაფის ურთიერთქმედება მუშაკთან მონაცემების ფორმადან (ვიზუალური კომპონენტებიდან) ნაკადზე გადასაცემად და პირიქით.

მაგალითი არ აცხადებს სრულყოფილებას, ის მხოლოდ აჩვენებს ძაფებს შორის ურთიერთქმედების უმარტივეს გზებს. საშუალებას აძლევს მომხმარებელს "სწრაფად დააბრმავოს" (ვინ იცის რა მძულს) სწორად მომუშავე მრავალსართულიანი პროგრამა.
მასში ყველაფერი დეტალურადაა აღწერილი (ჩემი აზრით), მაგრამ თუ თქვენ გაქვთ რაიმე შეკითხვა, დასვით.
მაგრამ კიდევ ერთხელ ვაფრთხილებ: ნაკადები ადვილი არ არის... თუ წარმოდგენა არ გაქვთ როგორ მუშაობს ეს ყველაფერი, მაშინ არის უზარმაზარი საფრთხე, რომ ხშირად ყველაფერი კარგად გამოგივათ, ზოგჯერ კი პროგრამა უფრო უცნაურად იქცევა. არასწორად დაწერილი მრავალსართულიანი პროგრამის ქცევა დიდად არის დამოკიდებული იმ უამრავ ფაქტორზე, რომელთა გამოსწორებაც ზოგჯერ შეუძლებელია.

ასე რომ მაგალითი. მოხერხებულობისთვის, მე მოვათავსე ორივე კოდი და დავამატე არქივი მოდულთან და ფორმის კოდთან ერთად

ერთეული ExThreadForm;

იყენებს
Windows, შეტყობინებები, SysUtils, ვარიანტები, კლასები, გრაფიკა, კონტროლი, ფორმები,
დიალოგები, StdCtrls;

// მუდმივები, რომლებიც გამოიყენება მონაცემთა ნაკადზე ფორმაში გამოყენებისას
// ფანჯრის შეტყობინებების გაგზავნა
კონსტ
WM_USER_SendMessageMetod = WM_USER + 10;
WM_USER_PostMessageMetod = WM_USER + 11;

ტიპი
// ძაფის კლასის აღწერა, tThread- ის შთამომავალი
tMyThread = კლასი (tThread)
პირადი
SyncDataN: მთელი რიცხვი;
SyncDataS: სიმებიანი;
პროცედურა SyncMetod1;
დაცული
პროცედურა შეასრულოს; გადაფარვა;
საჯარო
Param1: სიმებიანი;
Param2: მთელი რიცხვი;
Param3: ლოგიკური;
გაჩერდა: ლოგიკური;
LastRandom: მთელი რიცხვი;
IterationNo: მთელი რიცხვი;
ResultList: tStringList;

კონსტრუქტორის შექმნა (aParam1: String);
დესტრუქტორი განადგურება; გადაფარვა;
დასასრული;

// ფორმის აღწერა ნაკადის გამოყენებით
TForm1 = კლასი (TForm)
ლეიბლი 1: TLabel;
შენიშვნა 1: TMemo;
btn დაწყება: TButton;
btn გაჩერება: TButton;
რედაქტირება 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;

საჯარო
(საჯარო განცხადებები)
დასასრული;

ვარი
ფორმა 1: TForm1;

{
შეჩერებულია - აჩვენებს მონაცემების გადაცემას ფორმიდან ნაკადში.
დამატებითი სინქრონიზაცია არ არის საჭირო, რადგან ის მარტივია
ერთი სიტყვის ტიპი და დაწერილია მხოლოდ ერთი ძაფით.
}

პროცედურა TForm1.btnStartClick (გამგზავნი: TObject);
დაწყება
რანდომიზაცია (); // მიმდევრობის შემთხვევითობის უზრუნველყოფა Random () - არა აქვს საერთო ნაკადთან

// შექმენით ნაკადის ობიექტის მაგალითი, გადასცემთ მას შეყვანის პარამეტრს
{
ყურადღება!
ნაკადის კონსტრუქტორი ისეა დაწერილი, რომ ნაკადი იქმნება
შეჩერებულია, რადგან ის იძლევა:
1. გააკონტროლეთ მისი გაშვების მომენტი. ეს თითქმის ყოველთვის უფრო მოსახერხებელია, რადგან
საშუალებას გაძლევთ შექმნათ ნაკადი დაწყებამდეც კი, გაიაროთ მისი შეყვანა
პარამეტრები და ა.შ.
2. იმიტომ ბმული შექმნილი ობიექტისთვის შეინახება ფორმის ველში, მაშინ
ძაფის თვითგანადგურების შემდეგ (იხ. ქვემოთ) რომელიც ძაფის გაშვებისას
შეიძლება მოხდეს ნებისმიერ დროს, ეს ბმული არასწორი გახდება.
}
MyThread: = tMyThread.Create (Form1.Edit1.Text);

// თუმცა, მას შემდეგ, რაც თემა შეიქმნა შეჩერებული, მაშინ შეცდომებზე
// მისი ინიციალიზაციის დროს (დაწყებამდე), ჩვენ თვითონ უნდა გავანადგუროთ იგი
// რისთვისაც ჩვენ ვიყენებთ try / გარდა ბლოკის
სცადე

// ძაფის დამთავრების დამმუშავებლის მინიჭება, რომელშიც ჩვენ მივიღებთ
// ნაკადის მუშაობის შედეგები და მისი გადაწერა "გადაწერა"
MyThread.OnTerminate: = EventMyThreadOnTerminate;

// ვინაიდან შედეგები შეგროვდება OnTerminate- ში, ე.ი. თვითგანადგურებამდე
// ნაკადი მაშინ ჩვენ მოვიშორებთ მის განადგურების საზრუნავს
MyThread.FreeOnTerminate: = მართალია;

// ნაკადის ობიექტის ველების გავლით შეყვანის პარამეტრების მაგალითი, წერტილში
// დაუყონებლივ როცა არ მუშაობს.
// პირადად მე მირჩევნია ამის გაკეთება გადაფარებული პარამეტრების საშუალებით
// კონსტრუქტორი (tMyThread.Create)
MyThread.Param2: = StrToInt (Form1.Edit2.Text);

MyThread.Stopped: = ყალბი; // ერთგვარი პარამეტრიც, მაგრამ იცვლება
// ძაფის გაშვების დრო
გარდა
// ვინაიდან თემა ჯერ არ დაწყებულა და ვერ შეძლებს თვითგანადგურებას, ჩვენ მას "ხელით" გავანადგურებთ
FreeAndNil (MyThread);
// და შემდეგ ნება მიეცით გამონაკლისს, როგორც ყოველთვის
ამაღლება;
დასასრული;

// მას შემდეგ, რაც ძაფის ობიექტი წარმატებით შეიქმნა და დაკონფიგურირდა, დროა დაიწყოს იგი
MyThread. Resume;

ShowMessage ("ნაკადი დაიწყო");
დასასრული;

პროცედურა TForm1.btnStopClick (გამგზავნი: TObject);
დაწყება
// თუ ძაფის მაგალითი ჯერ კიდევ არსებობს, მაშინ სთხოვეთ შეჩერდეს
// და, ზუსტად "იკითხე". პრინციპში, ჩვენ ასევე შეგვიძლია "ვაიძულოთ", მაგრამ ეს მოხდება
// უკიდურესად გადაუდებელი ვარიანტი, რომელიც მოითხოვს ამ ყველაფრის ნათელ გაგებას
// ნაკადი სამზარეულო. ამიტომ, აქ არ განიხილება.
თუ მინიჭებული (MyThread) მაშინ
MyThread.Stopped: = მართალია
სხვა
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. წარწერა: = ფორმატი ("% 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);
დაწყება
// შექმენით შეჩერებული ნაკადის მაგალითი (იხილეთ კომენტარი ინსტანციის დროს)
მემკვიდრეობით შექმნა (ჭეშმარიტი);

// შიდა ობიექტების შექმნა (საჭიროების შემთხვევაში)
ResultList: = tStringList.Create;

// მიიღეთ საწყისი მონაცემები.

// პარამეტრის გავლით შეყვანილი მონაცემების კოპირება
Param1: = aParam1;

// ნაკადი ობიექტის კონსტრუქტორში VCL კომპონენტებისგან შეყვანის მონაცემების მიღების მაგალითი
// ეს მისაღებია ამ შემთხვევაში, ვინაიდან კონსტრუქტორი ეწოდება კონტექსტში
// მთავარი თემა. აქედან გამომდინარე, VCL კომპონენტების წვდომა შესაძლებელია აქ.
// მაგრამ, მე არ მომწონს ეს, რადგან მე ვფიქრობ, რომ ცუდია, როცა თემა რაღაცას იცნობს
// რაღაც ფორმის შესახებ. მაგრამ, რისი გაკეთება არ შეგიძლია დემონსტრაციისთვის.
Param3: = Form1.CheckBox1.Checked;
დასასრული;

დესტრუქტორი tMyThread.Destroy;
დაწყება
// შიდა ობიექტების განადგურება
FreeAndNil (ResultList);
// განადგურება ბაზის tThread
მემკვიდრეობით მიღებული;
დასასრული;

პროცედურა tMyThread.Eecute;
ვარი
t: კარდინალი;
s: სიმებიანი;
დაწყება
IterationNo: = 0; // შედეგების მრიცხველი (ციკლის ნომერი)

// ჩემს მაგალითში, ძაფის სხეული არის მარყუჟი, რომელიც მთავრდება
// ან შეწყვეტის გარე "მოთხოვნით" გადავიდა ცვლადი პარამეტრით Stopped,
// ან მხოლოდ 5 მარყუჟის გაკეთებით
// ჩემთვის უფრო სასიამოვნოა ამის დაწერა "მარადიული" მარყუჟის საშუალებით.

სანამ ჭეშმარიტი დაიწყება

Inc (IterationNo); // შემდეგი ციკლის ნომერი

ბოლო შემთხვევითი: = შემთხვევითი (1000); // გასაღების ნომერი - ნაკადიდან ფორმაზე პარამეტრების გადაცემის დემონსტრირება

T: = შემთხვევითი (5) +1; // დრო, რომლისთვისაც ჩვენ გვეძინება, თუ არ დავასრულებთ

// სულელური სამუშაო (დამოკიდებულია შეყვანის პარამეტრზე)
თუ არა Param3 მაშინ
Inc (Param2)
სხვა
დეკემბერი (Param2);

// შუალედური შედეგის ჩამოყალიბება
s: = ფორმატი ("% s% 5d% s% d% d",
);

// დაამატეთ შუალედური შედეგი შედეგების ჩამონათვალს
ResultList.Add (s);

//// ფორმა შუალედური შედეგის გადაცემის მაგალითები

//// სინქრონიზებული მეთოდის გავლა - კლასიკური გზა
//// ნაკლოვანებები:
//// - სინქრონიზებული მეთოდი ჩვეულებრივ არის ნაკადის კლასის მეთოდი (წვდომა
//// ნაკადის ობიექტის სფეროებში), მაგრამ, ფორმის ველზე წვდომისათვის, ეს უნდა
//// "იცოდე" მის შესახებ და მისი სფეროები (ობიექტები), რაც ჩვეულებრივ არ არის ძალიან კარგი
//// პროგრამის ორგანიზაციის თვალსაზრისი.
//// - მიმდინარე თემა შეჩერდება შესრულების დასრულებამდე
//// სინქრონიზებული მეთოდი.

//// უპირატესობები:
//// - სტანდარტული და მრავალმხრივი
//// - სინქრონიზებული მეთოდით, შეგიძლიათ გამოიყენოთ
//// ნაკადის ობიექტის ყველა ველი.
// პირველ რიგში, საჭიროების შემთხვევაში, თქვენ უნდა შეინახოთ გადატანილი მონაცემები
// ობიექტის ობიექტის სპეციალური ველები.
SyncDataN: = IterationNo;
SyncDataS: = "სინქრონიზაცია" + s;
// და შემდეგ უზრუნველყოს სინქრონიზებული მეთოდის ზარი
სინქრონიზაცია (SyncMetod1);

//// გაგზავნა სინქრონული შეტყობინების გაგზავნით (SendMessage)
//// ამ შემთხვევაში, მონაცემების გადაცემა შესაძლებელია როგორც შეტყობინების პარამეტრებით (LastRandom),
//// და ობიექტის ველების გავლით, ინსტანციის მისამართის გაგზავნა შეტყობინების პარამეტრში
//// ნაკადის ობიექტი - მთელი რიცხვი (თვით).
//// ნაკლოვანებები:
//// - ძაფმა უნდა იცოდეს ფორმის ფანჯრის სახელური
//// - როგორც სინქრონიზაციისას, მიმდინარე ძაფი შეჩერდება მანამ
//// მთავრდება წერილის დამუშავება მთავარი ძაფით
//// - მოითხოვს პროცესორის მნიშვნელოვან რაოდენობას თითოეული ზარისთვის
//// (ძაფების გადართვა) ამიტომ ძალიან ხშირი ზარი არასასურველია
//// უპირატესობები:
//// - როგორც სინქრონიზაციისას, შეტყობინების დამუშავებისას შეგიძლიათ გამოიყენოთ
//// ნაკადის ობიექტის ყველა ველი (თუ, რა თქმა უნდა, მისი მისამართი გავიდა)


//// დაიწყე თემა.
SendMessage (Form1.Handle, WM_USER_SendMessageMetod, Integer (Self), LastRandom);

//// გადაცემა ასინქრონული შეტყობინების გაგზავნით (PostMessage)
//// ვინაიდან ამ შემთხვევაში, სანამ შეტყობინება მიიღება მთავარ ძაფზე,
//// გაგზავნის ნაკადი შეიძლება უკვე დასრულდეს, გაიაროს ინსტანციის მისამართი
//// ნაკადის ობიექტი არასწორია!
//// ნაკლოვანებები:
//// - ძაფმა უნდა იცოდეს ფორმის ფანჯრის სახელური;
//// - ასინქრონიზმის გამო, მონაცემთა გადაცემა შესაძლებელია მხოლოდ პარამეტრების საშუალებით
//// შეტყობინებები, რაც მნიშვნელოვნად ართულებს მონაცემების გადაცემას, რომელსაც აქვს ზომა
//// ორზე მეტი მანქანა სიტყვა. მოსახერხებელია გამოვიყენოთ მთელი რიცხვი და ა.შ.
//// უპირატესობები:
//// - წინა მეთოდებისგან განსხვავებით, მიმდინარე თემა არ იქნება
//// შეჩერებულია და დაუყოვნებლივ განაახლებს შესრულებას
//// - სინქრონიზებული ზარისგან განსხვავებით, შეტყობინებების დამმუშავებელი
//// არის ფორმა მეთოდი, რომელსაც უნდა ჰქონდეს ცოდნა ნაკადის ობიექტის შესახებ,
//// ან საერთოდ არაფერი იცოდეთ ნაკადის შესახებ, თუ მონაცემები გადაეცემა მხოლოდ
//// შეტყობინების პარამეტრების საშუალებით. ანუ თემამ შეიძლება არაფერი იცოდეს ფორმის შესახებ.
//// ზოგადად - მხოლოდ მისი სახელური, რომლის პარამეტრების გადატანა შესაძლებელია ადრე
//// დაიწყე თემა.
PostMessage (Form1.Handle, WM_USER_PostMessageMetod, IterationNo, LastRandom);

//// შეამოწმეთ შესაძლო დასრულებისთვის

// შეამოწმეთ დასრულების პარამეტრით
თუ შეჩერდა მაშინ დაარღვიე;

// შეამოწმეთ დასრულების შემთხვევები
თუ IterationNo> = 10 მაშინ შესვენება;

ძილი (t * 1000); // დაიძინე t წამით
დასასრული;
დასასრული;

პროცედურა tMyThread.SyncMetod1;
დაწყება
// ამ მეთოდს ეწოდება Synchronize მეთოდი.
// ანუ, იმისდა მიუხედავად, რომ ეს არის tMyThread ძაფის მეთოდი,
// ის მუშაობს პროგრამის ძირითადი ძაფის კონტექსტში.
// მაშასადამე, მას შეუძლია გააკეთოს ყველაფერი, კარგად, ან თითქმის ყველაფერი :)
// მაგრამ დაიმახსოვრე, არ ღირს აქ დიდი ხნით "არევა"

// გავლილი პარამეტრები, ჩვენ შეგვიძლია ამონაწერი სპეციალური ველებიდან, სადაც გვაქვს
// შენახულია ზარამდე.
Form1.Label1.Caption: = SyncDataS;

// ნაკადი ობიექტის სხვა სფეროებიდან, მაგალითად, ასახავს მის ამჟამინდელ მდგომარეობას
Form1.Label2.caption: = ფორმატი ("% d% d",);
დასასრული;

ზოგადად, მაგალითს წინ უძღოდა ჩემი შემდეგი მსჯელობა თემაზე ...

პირველ რიგში:
დელფის მრავალმხრივი პროგრამირების ყველაზე მნიშვნელოვანი წესი არის:
არა-ძირითადი ძაფის კონტექსტში, თქვენ ვერ შეხვალთ ფორმების თვისებებსა და მეთოდებზე და მართლაც ყველა კომპონენტზე, რომლებიც "იზრდება" tWinControl– დან.

ეს ნიშნავს (გარკვეულწილად გამარტივებულია), რომ არც შესრულების მეთოდით მემკვიდრეობით მიღებული TThread- დან და არც სხვა მეთოდებში / პროცედურებში / ფუნქციებში, რომელსაც ეწოდება Execute, აკრძალულიაპირდაპირ წვდომა ვიზუალური კომპონენტების ნებისმიერ თვისებებსა და მეთოდებზე.

როგორ გავაკეთოთ ეს სწორად.
არ არსებობს ერთიანი რეცეპტები. უფრო ზუსტად, იმდენი და განსხვავებული ვარიანტია, რომ კონკრეტული შემთხვევის მიხედვით უნდა აირჩიო. ამიტომ, ისინი ეხება სტატიას. მისი წაკითხვისა და გაგების შემდეგ, პროგრამისტს შეეძლება გაიგოს და როგორ გააკეთოს ეს კონკრეტულ შემთხვევაში.

მოკლედ თქვენს თითებზე:

ყველაზე ხშირად, მრავალსართულიანი პროგრამა ხდება მაშინ, როდესაც აუცილებელია რაიმე სახის გრძელვადიანი სამუშაოს შესრულება, ან როდესაც შესაძლებელია ერთდროულად რამდენიმე საქმის გაკეთება, რაც პროცესორებს მძიმედ არ ატვირთავს.

პირველ შემთხვევაში, ძირითადი ძაფის შიგნით სამუშაოს განხორციელება იწვევს მომხმარებლის ინტერფეისის „შენელებას“ - სანამ სამუშაოები სრულდება, შეტყობინებების მარყუჟი არ არის შესრულებული. შედეგად, პროგრამა არ პასუხობს მომხმარებლის ქმედებებს და ფორმა არ არის შედგენილი, მაგალითად, მომხმარებლის გადაადგილების შემდეგ.

მეორე შემთხვევაში, როდესაც მუშაობა გულისხმობს გარე სამყაროსთან აქტიურ გაცვლას, მაშინ იძულებითი "გათიშვის დროს". სანამ ელოდებით მონაცემების მიღებას / გაგზავნას, შეგიძლიათ სხვა რამის გაკეთება პარალელურად, მაგალითად, ისევ, მონაცემების გაგზავნა / მიღება.

არის სხვა შემთხვევებიც, მაგრამ უფრო იშვიათად. თუმცა, ამას მნიშვნელობა არ აქვს. ახლა ამაზე არ არის საუბარი.

ახლა, როგორ წერია ეს ყველაფერი. ბუნებრივია, განიხილება გარკვეული ყველაზე ხშირი შემთხვევა, გარკვეულწილად განზოგადებული. Ისე.

ცალკეულ თემაში შესრულებულ სამუშაოს, ზოგადად, აქვს ოთხი ერთეული (არ ვიცი როგორ დავარქვა ეს უფრო ზუსტად):
1. საწყისი მონაცემები
2. რეალურად თავად ნამუშევარი (შეიძლება დამოკიდებული იყოს საწყის მონაცემებზე)
3. შუალედური მონაცემები (მაგალითად, ინფორმაცია სამუშაოების მიმდინარე მდგომარეობის შესახებ)
4. გამომავალი მონაცემები (შედეგი)

ყველაზე ხშირად, ვიზუალური კომპონენტები გამოიყენება მონაცემების უმეტესობის წასაკითხად და საჩვენებლად. მაგრამ, როგორც ზემოთ აღვნიშნეთ, თქვენ პირდაპირ ვერ შეხვალთ ვიზუალურ კომპონენტებზე ნაკადიდან. Როგორ უნდა იყოს?
დელფის დეველოპერები გვთავაზობენ TThread კლასის Synchronize მეთოდის გამოყენებას. აქ მე არ აღვწერ როგორ გამოვიყენო - ამისათვის არის ზემოთ ნახსენები სტატია. ნება მომეცით მხოლოდ ვთქვა, რომ მისი გამოყენება, თუნდაც სწორი, ყოველთვის არ არის გამართლებული. არის ორი პრობლემა:

პირველ რიგში, მეთოდის სხეული, რომელსაც ეწოდება Synchronize, ყოველთვის შესრულებულია ძირითადი ძაფის კონტექსტში და, შესაბამისად, სანამ ის არის შესრულებული, ისევ ფანჯრის შეტყობინებების მარყუჟი არ არის შესრულებული. ამიტომ, ის სწრაფად უნდა შესრულდეს, წინააღმდეგ შემთხვევაში, ჩვენ მივიღებთ ყველა იმავე პრობლემას, როგორც ერთი ხრახნიანი განხორციელებით. იდეალურ შემთხვევაში, მეთოდი, რომელსაც უწოდებენ Synchronize- ს, ზოგადად უნდა იქნას გამოყენებული მხოლოდ ვიზუალური ობიექტების თვისებებსა და მეთოდებზე წვდომისათვის.

მეორეც, სინქრონიზაციის მეთოდის განხორციელება არის "ძვირადღირებული" სიამოვნება ძაფებს შორის ორი გადართვის საჭიროების გამო.

უფრო მეტიც, ორივე პრობლემა ერთმანეთთან არის დაკავშირებული და იწვევს წინააღმდეგობას: ერთი მხრივ, პირველის მოსაგვარებლად აუცილებელია "დაფქვა" მეთოდების სინქრონიზაციის გზით, ხოლო მეორე მხრივ, მათ ხშირად უნდა დაურეკოთ, დაკარგონ ძვირფასი პროცესორის რესურსები.

ამიტომ, როგორც ყოველთვის, აუცილებელია გონივრულად მივუდგეთ და სხვადასხვა შემთხვევებში გამოვიყენოთ გარე სამყაროსთან ნაკადის ურთიერთქმედების სხვადასხვა გზა:

საწყისი მონაცემები
ყველა მონაცემი, რომელიც გადადის ნაკადზე და არ იცვლება მისი მუშაობის დროს, უნდა გადაეცეს მის დაწყებამდეც, ე.ი. ნაკადის შექმნისას. ძაფის სხეულში გამოსაყენებლად, თქვენ უნდა გააკეთოთ მათი ადგილობრივი ასლი (ჩვეულებრივ TThread შთამომავლის ველში).
თუ არსებობს საწყისი მონაცემები, რომლებიც შეიძლება შეიცვალოს ძაფის გაშვებისას, მაშინ ასეთ მონაცემებზე წვდომა უნდა მოხდეს ან სინქრონიზებული მეთოდებით (მეთოდები, რომლებიც სინქრონიზებულია), ან ძაფის ობიექტის ველების მეშვეობით (TThread– ის შთამომავალი). ეს უკანასკნელი მოითხოვს გარკვეულ სიფრთხილეს.

შუალედური და გამომავალი მონაცემები
აქ, კიდევ ერთხელ, არსებობს რამდენიმე გზა (ჩემი უპირატესობის მიხედვით):
- შეტყობინებების ასინქრონული გაგზავნის მეთოდი პროგრამის მთავარ ფანჯარაში.
ის ჩვეულებრივ გამოიყენება პროცესის მიმდინარეობის შესახებ შეტყობინებების გასაგზავნად პროგრამის მთავარ ფანჯარაში, მცირე რაოდენობის მონაცემების გადაცემით (მაგალითად, დასრულების პროცენტი)
- შეტყობინებების სინქრონული გაგზავნის მეთოდი პროგრამის მთავარ ფანჯარაში.
ჩვეულებრივ გამოიყენება იმავე მიზნებისათვის, როგორც ასინქრონული გაგზავნა, მაგრამ გაძლევთ საშუალებას გადაიტანოთ უფრო დიდი რაოდენობით მონაცემები, ცალკეული ასლის შექმნის გარეშე.
- სინქრონიზებული მეთოდები, თუ ეს შესაძლებელია, აერთიანებს რაც შეიძლება მეტი მონაცემის გადაცემას ერთ მეთოდში.
ის ასევე შეიძლება გამოყენებულ იქნას ფორმადან მონაცემების მოსაპოვებლად.
- ნაკადის ობიექტის ველების საშუალებით, რაც უზრუნველყოფს ურთიერთგამომრიცხავ წვდომას.
უფრო დეტალური ინფორმაცია შეგიძლიათ იხილოთ სტატიაში.

ეჰ. მოკლედ არ გამოვიდა