Windows үйлдлийн систем дэх урсгалыг синхрончлох хэрэгслүүд (чухал хэсгүүд, мутексууд, семафорууд, үйл явдлууд). Windows дахь синхрончлолын объект Үйл явдлуудыг ашиглан процессуудыг синхрончлох

Лекц No 9. Процесс ба урсгалуудыг синхрончлох

1. Синхрончлолын зорилго, арга хэрэгсэл.

2. Синхрончлолын механизмууд.

1.Синхрончлолын зорилго, арга хэрэгсэл

Үйлдлийн системийн хэрэгслүүдийн нэлээд өргөн хүрээтэй анги байдаг бөгөөд энэ нь процессууд болон урсгалуудын харилцан синхрончлолыг хангадаг. Утасны синхрончлолын хэрэгцээ нь зөвхөн олон програмын үйлдлийн системд үүсдэг бөгөөд компьютерийн системийн техник хангамж, мэдээллийн нөөцийг хамтран ашиглахтай холбоотой юм. Сэдвүүдийн хооронд өгөгдөл солилцох, өгөгдөл хуваалцах, процессор болон оролт гаралтын төхөөрөмжүүдэд хандах үед уралдаан, түгжрэлээс зайлсхийхийн тулд синхрончлол хийх шаардлагатай.

Олон үйлдлийн системд эдгээр хэрэгслүүдийг процесс хоорондын харилцааны хэрэгсэл (IPC) гэж нэрлэдэг бөгөөд энэ нь "утас" гэсэн ойлголттой холбоотой "процесс" гэсэн ойлголтын түүхэн ач холбогдлыг тусгасан байдаг. Ер нь IPC хэрэгслүүд нь процесс хоорондын синхрончлолын хэрэгслүүдээс гадна процесс хоорондын өгөгдөл солилцох хэрэгслүүдийг агуулдаг.

Олон програмчлалын орчинд урсгалын гүйцэтгэл нь үргэлж асинхрон байдаг. Тодорхой цаг хугацааны явцад үйл явц ямар шатандаа явагдахыг бүрэн тодорхой хэлэх нь маш хэцүү байдаг. Ганц програмын горимд ч гэсэн даалгаврыг гүйцэтгэхэд шаардагдах хугацааг нарийн тооцоолох боломжгүй байдаг. Энэ хугацаа нь ихэнх тохиолдолд эх өгөгдлийн утгаас ихээхэн хамаардаг бөгөөд энэ нь мөчлөгийн тоо, програмын салбарлах чиглэл, оролт гаралтын үйлдлүүдийн гүйцэтгэх хугацаа гэх мэт. эхлүүлсэн нь өөр байж болох тул гүйцэтгэх хугацаа нь бие даасан үе шатууд болон даалгавар нь бүхэлдээ маш тодорхойгүй утга юм.


Илүү тодорхойгүй зүйл бол олон програмчлалын систем дэх програмын гүйцэтгэлийн хугацаа юм. Сэдвүүд тасалдсан мөчүүд, хуваалцсан нөөцөд зориулж дараалалд зарцуулдаг цаг хугацаа, утсыг гүйцэтгэхэд сонгосон дараалал - эдгээр бүх үйл явдал нь олон нөхцөл байдлын нийлсэн үр дүн бөгөөд санамсаргүй байдлаар тайлбарлаж болно. Хамгийн сайндаа тооцоолох үйл явцын магадлалын шинж чанарыг, жишээлбэл, тодорхой хугацаанд дуусгах магадлалыг тооцоолж болно.

Тиймээс ерөнхий тохиолдолд (программист тэдгээрийг синхрончлох тусгай арга хэмжээ аваагүй тохиолдолд) утаснууд бие биентэйгээ асинхрон байдлаар бие даан урсдаг. Энэ нь нийтлэг програмын кодыг гүйцэтгэдэг ижил процессын хэлхээний хувьд, мөн тус бүр өөрийн програмыг ажиллуулдаг өөр өөр процесс дахь thread-ийн хувьд үнэн юм.

Процессууд эсвэл урсгалуудын хоорондын аливаа харилцан үйлчлэл нь тэдгээртэй холбоотой байдаг синхрончлол,Энэ нь тодорхой үйл явдал тохиолдох хүртэл урсгалыг түр зогсоож, дараа нь энэ үйл явдал тохиолдоход идэвхжүүлэх замаар тэдгээрийн хурдыг зохицуулахаас бүрдэнэ. Синхрончлол нь нөөц хуваалцах эсвэл өгөгдөл солилцохоос үл хамааран аливаа урсгалын харилцан үйлчлэлийн цөм юм. Жишээлбэл, хүлээн авагч хэлхээ нь зөвхөн илгээж буй хэлхээгээр буферлэгдсэний дараа өгөгдөлд хандах ёстой. Хэрэв хүлээн авагч утас буферт орохоосоо өмнө өгөгдөлд хандсан бол түүнийг түр зогсоох шаардлагатай.

Техник хангамжийн нөөцийг хуваалцах үед синхрончлол нь зайлшгүй шаардлагатай. Жишээлбэл, идэвхтэй урсгал нь цуваа порт руу нэвтрэх шаардлагатай бөгөөд хүлээгдэж буй төлөвт байгаа өөр хэлхээ энэ порттой онцгой горимд ажиллаж байх үед үйлдлийн систем идэвхтэй урсгалыг түдгэлзүүлж, шаардлагатай порт хүртэл идэвхжүүлдэггүй. үнэ төлбөргүй байдаг. Компьютерийн системийн гаднах үйл явдлуудтай синхрончлол хийх, жишээлбэл, Ctrl + C товчлуурын хослолыг дарахад үзүүлэх хариу үйлдэл нь ихэвчлэн шаардлагатай байдаг.

Секунд тутамд нөөцийг хуваарилах, гаргахтай холбоотой олон зуун үйл явдал системд тохиолддог бөгөөд үйлдлийн систем нь системд болж буй үйл явдлуудтай хэлхээг синхрончлох боломжийг олгодог найдвартай, үр ашигтай хэрэгсэлтэй байх ёстой.

Хэрэглээний програмын хэлхээг синхрончлохын тулд программист өөрийн синхрончлолын хэрэгсэл, техник, үйлдлийн системийн хэрэгслийг хоёуланг нь ашиглаж болно. Жишээлбэл, нэг хэрэглүүрийн процессын хоёр урсгал нь аль алинд нь ашиглах боломжтой глобал логикийн хувьсагчийг ашиглан ажлаа зохицуулж чаддаг бөгөөд энэ нь ямар нэгэн үйл явдал тохиолдоход нэгд нь тохируулагддаг, жишээлбэл, нэг хэлхээ нөгөө нь үргэлжлүүлэн ажиллахад шаардлагатай өгөгдлийг үүсгэдэг. Гэсэн хэдий ч ихэнх тохиолдолд үйлдлийн системээс системийн дуудлагын хэлбэрээр хангадаг синхрончлолын хэрэгслүүд нь илүү үр дүнтэй, эсвэл бүр цорын ганц боломжтой байдаг. Тиймээс янз бүрийн процесст хамаарах утаснууд бие биенийхээ ажилд ямар ч байдлаар саад болохгүй. Үйлдлийн системийн зуучлалгүйгээр тэд бие биенээ түдгэлзүүлэх эсвэл үйл явдлын талаар бие биедээ мэдэгдэх боломжгүй. Синхрончлолын хэрэгслүүдийг үйлдлийн систем нь зөвхөн хэрэглээний процессуудыг синхрончлоход төдийгүй дотоод хэрэгцээнд нь ашигладаг.

Ерөнхийдөө үйлдлийн систем хөгжүүлэгчид программ болон системийн программистуудын мэдэлд синхрончлолын өргөн хүрээний хэрэгслээр хангадаг. Эдгээр хэрэгслүүд нь илүү нарийн төвөгтэй хэрэгслүүдийг илүү энгийн хэрэгслүүдийн үндсэн дээр бүтээхэд шатлал үүсгэж болох ба функциональ шинж чанартай байж болно, жишээлбэл, нэг процессын хэлхээг синхрончлох хэрэгсэл, өгөгдөл солилцох үед янз бүрийн процессын хэлхээг синхрончлох хэрэгсэл гэх мэт. Ихэнхдээ системийн янз бүрийн дуудлагын синхрончлолын функцууд давхцдаг тул програмист өөрийн хувийн сонголтоос хамааран нэг асуудлыг шийдэхийн тулд хэд хэдэн дуудлага ашиглаж болно.


Синхрончлол, уралдааны хэрэгцээ

Олон урсгалтай систем дэх синхрончлолын асуудлыг үл тоомсорлох нь асуудлыг буруу шийдвэрлэх эсвэл бүр системийн эвдрэлд хүргэж болзошгүй юм. Жишээ нь (Зураг 4.16) тодорхой аж ахуйн нэгжийн үйлчлүүлэгчдийн мэдээллийн санг хадгалах ажлыг авч үзье. Үйлчлүүлэгч бүрт мэдээллийн санд тусдаа бүртгэл хуваарилагдсан бөгөөд үүнд бусад талбаруудын дотор Захиалга, Төлбөрийн талбарууд багтдаг. Мэдээллийн санг хөтөлдөг программ нь үйлчлүүлэгчдээс хүлээн авсан захиалгын мэдээллийг мэдээллийн санд оруулах А thread, нэхэмжлэхийн хэрэглэгчийн төлбөрийн талаарх мэдээллийг мэдээллийн санд бүртгэдэг thread B зэрэг хэд хэдэн урсгалтай нэг процесс хэлбэрээр бүтээгдсэн. Эдгээр хоёр хэлхээ нь ижил алгоритмыг ашиглан нийтлэг мэдээллийн сангийн файл дээр хамтран ажилладаг бөгөөд үүнд гурван алхам багтдаг.

2. Захиалга (А урсгалын хувьд) эсвэл Төлбөрийн (Б урсгалын хувьд) талбарт шинэ утгыг оруулна уу.

3. Өөрчлөгдсөн бичлэгийг өгөгдлийн сангийн файл руу буцаана.

https://pandia.ru/text/78/239/images/image002_238.gif" өргөн "505" өндөр "374 src=">

Цагаан будаа. 4.17.Асуудлыг шийдвэрлэх үр дүнд харьцангуй урсгалын хурдны нөлөөлөл

Чухал хэсэг

Thread синхрончлолын чухал ойлголт бол програмын "чухал хэсэг" гэсэн ойлголт юм. Чухал хэсэгЭнэ нь тухайн хэсгийн гүйцэтгэл хараахан дуусаагүй байхад тухайн програмын хэсэгтэй холбоотой хувьсагчийг бусад хэлхээнүүд өөрчилсөн тохиолдолд гүйцэтгэлийн үр дүн нь урьдчилан таамаглах аргагүй өөрчлөгдөж болох програмын хэсэг юм. Чухал хэсэг нь үргэлж тодорхой холбоотой тодорхойлогддог чухал өгөгдөлзохицуулалтгүй өөрчилсөн тохиолдолд хүсээгүй үр дагавар гарч болзошгүй. Өмнөх жишээнд чухал өгөгдөл нь мэдээллийн сангийн файлын бичлэгүүд байсан. Чухал өгөгдөлтэй ажилладаг бүх хэлхээ нь тодорхойлогдсон чухал хэсэгтэй байх ёстой. Янз бүрийн хэлхээн дэх чухал хэсэг нь ерөнхийдөө өөр өөр тушаалуудын дарааллаас бүрддэг болохыг анхаарна уу.

Уралдааны чухал өгөгдөлд үзүүлэх нөлөөг арилгахын тулд ямар ч үед тухайн өгөгдөлтэй холбоотой чухал хэсэгт зөвхөн нэг утас байгаа эсэхийг баталгаажуулах шаардлагатай. Энэ хэлхээ идэвхтэй эсвэл түр зогссон төлөвт байгаа эсэх нь хамаагүй. Энэ техникийг нэрлэдэг бие биенээ үгүйсгэх.Үйлдлийн систем нь бие биенээ үгүйсгэх янз бүрийн аргыг ашигладаг. Зарим аргууд нь зөвхөн нэг процессын урсгалууд чухал хэсэгт ороход харилцан хамааралгүй байх үед тохиромжтой байдаг бол зарим нь өөр өөр процессуудын хэлхээг харилцан хасах боломжийг олгодог.

Харилцан тусгаарлалтыг хангах хамгийн энгийн бөгөөд хамгийн үр ашиггүй арга бол үйлдлийн систем нь чухал хэсэгт байх үед утас нь ямар ч тасалдлыг идэвхгүй болгох явдал юм. Гэсэн хэдий ч энэ аргыг бараг ашигладаггүй, учир нь системийг удирдахын тулд хэрэглэгчийн хэлхээнд итгэх нь аюултай байдаг - энэ нь процессорыг удаан хугацаагаар эзэлдэг бөгөөд хэрэв чухал хэсэгт утас гацвал систем бүхэлдээ гацах болно. тасалдлыг хэзээ ч зөвшөөрөхгүй.

2. Синхрончлолын механизмууд.

Хувьсагчдыг блоклох

Нэг процессын хэлхээг синхрончлохын тулд хэрэглээний программист глобалыг ашиглаж болно блоклох хувьсагч.Программист нь үйлдлийн системийн бүх утас руу шууд хандах боломжтой эдгээр хувьсагчтай ажилладаг бөгөөд үйлдлийн системийн системийн дуудлагад хандахгүйгээр ажилладаг.

Энэ нь бүх баталгаажуулалт, суулгалтын үйл ажиллагааны туршид тасалдлыг идэвхгүй болгоно.

Дээр дурдсан байдлаар бие биенээ үгүйсгэх нь мэдэгдэхүйц сул талтай: нэг хэлхээ чухал хэсэгт байх үед процессор руу нэвтрэх боломжтой ижил нөөц шаардлагатай өөр нэг хэлхээ нь блоклох хувьсагчийг тасралтгүй санал асуулгаж, процессорын цагийг үрэх болно. түүн дээр хуваарилагдсан бөгөөд үүнийг өөр хэлхээг гүйцэтгэхэд ашиглаж болно. Энэ дутагдлыг арилгахын тулд олон үйлдлийн системүүд чухал хэсгүүдтэй ажиллах тусгай системийн дуудлагуудыг өгдөг.

Зураг дээр. Зураг 4.19-д эдгээр функцууд нь Windows NT үйлдлийн системд харилцан үл хамаарах байдлыг хэрхэн хэрэгжүүлж байгааг харуулж байна. Чухал өгөгдлийг өөрчилж эхлэхээс өмнө хэлхээ нь EnterCriticalSection() системийн дуудлагыг гаргадаг. Энэ дуудлага нь эхлээд өмнөх тохиолдлын адил чухал нөөцийн төлөвийг тусгасан блоклох хувьсагчийн шалгалтыг гүйцэтгэдэг. Хэрэв системийн дуудлага нь нөөц завгүй байгааг тогтоовол (F(D) = 0), өмнөх тохиолдлоос ялгаатай нь энэ нь мөчлөгт санал асуулга хийхгүй, харин хэлхээг хүлээгдэж буй төлөвт (D) оруулж, энэ хэлхээг тэмдэглэнэ. холбогдох нөөц бэлэн болсон үед идэвхжүүлсэн байх ёстой. Энэ нөөцийг одоо ашиглаж байгаа утас нь чухал хэсгээс гарсны дараа LeaveCriticalSectionO системийн функцийг гүйцэтгэх ёстой бөгөөд үүний үр дүнд блоклох хувьсагч нь нөөцийн чөлөөт төлөвт тохирох утгыг авдаг (F(D) = 1), мөн үйлдлийн систем нь энэ нөөцийн хэлхээг хүлээж байгаа хүмүүсийн дарааллыг харж, эхний хэлхээг дарааллаас бэлэн төлөв рүү шилжүүлдэг.

Нэмэлт зардал" href="/text/category/nakladnie_rashodi/" rel="bookmark">Чуулганы хэсэг рүү орох, гарах функцийг хэрэгжүүлэхэд зориулсан үйлдлийн системийн нэмэлт зардал нь олж авсан хэмнэлтээс давж болно.

Семафорууд

Блоклох хувьсагчдын ерөнхий дүгнэлтийг гэж нэрлэдэг Dijkstra семафорууд.Дийкстра хоёртын хувьсагчийн оронд сөрөг бус бүхэл тоон утгыг авч болох хувьсагчдыг ашиглахыг санал болгосон. Тооцооллын процессыг синхрончлоход ашигладаг ийм хувьсагчдыг семафор гэж нэрлэдэг.

Семафортой ажиллахын тулд уламжлалт байдлаар P ба V гэж тэмдэглэсэн хоёр командыг оруулав. S хувьсагч нь семафорыг илэрхийлнэ. Дараа нь V(S) ба P(S) үйлдлүүдийг дараах байдлаар тодорхойлно.

* V(S): Хувьсагч S нэг үйлдэл болгон 1-ээр нэмэгддэг. Дээж авах, барих, хадгалах ажлыг тасалдуулах боломжгүй. Энэ үйлдлийг гүйцэтгэх үед S хувьсагч руу бусад урсгалууд ханддаггүй.

* P(S): Хэрэв боломжтой бол S-г 1-ээр бууруулна. Хэрэв 5=0 бөгөөд сөрөг бус бүхэл тоонуудын бүсэд үлдэхийн зэрэгцээ S-ийг багасгах боломжгүй бол урсгалыг дуудах үйлдэл P энэ бууралт боломжтой болтол хүлээнэ. Амжилттай шалгах, бууруулах нь мөн хуваагдашгүй үйл ажиллагаа юм.

V ба P командыг гүйцэтгэх явцад ямар ч тасалдлыг зөвшөөрөхгүй.

Онцгой тохиолдолд S семафор нь зөвхөн 0 ба 1 утгыг авч чаддаг бол энэ нь блоклох хувьсагч болж хувирдаг бөгөөд энэ шалтгааны улмаас үүнийг хоёртын семафор гэж нэрлэдэг. Үйл ажиллагаа P нь түүнийг гүйцэтгэж байгаа утсыг хүлээгдэж буй байдалд оруулах боломжтой бол V үйл ажиллагаа нь зарим тохиолдолд P үйл ажиллагаанаас болж түр зогссон өөр хэлхээг сэрээж болзошгүй.

Мультипрограмчлалын горимд ажиллаж байгаа хоёр урсгалын харилцан үйлчлэлийн сонгодог жишээг ашиглан семафорын хэрэглээг авч үзье, тэдгээрийн нэг нь буферийн санд өгөгдөл бичиж, нөгөө нь буферийн сангаас уншдаг. Буферийн сан нь N буферээс бүрдэх ба тус бүр нь нэг оруулга агуулж болно. Ерөнхийдөө, writer thread болон reader thread нь өөр өөр хурдтай байж, янз бүрийн эрчимтэй буферийн санд хандах боломжтой. Нэг хугацаанд бичих хурд нь унших хурдаас давж магадгүй, нөгөө үед эсрэгээр. Хамтдаа зөв ажиллахын тулд бүх буфер завгүй үед зохиолчийн хэлхээ түр зогсох ёстой бөгөөд дор хаяж нэг буфер чөлөөлөгдөх үед сэрэх ёстой. Үүний эсрэгээр уншигчийн хэлхээг бүх буфер хоосон үед түр зогсоож, дор хаяж нэг бичих гарч ирэх үед сэрэх ёстой.

Хоёр семафорыг танилцуулъя: e - хоосон буферын тоо, f - дүүргэсэн буферийн тоо, эхний төлөвт e = N, a f = 0. Дараа нь нийтлэг буферийн сан бүхий thread-уудын ажиллагааг дараах байдлаар дүрсэлж болно. (Зураг 4.20).

Зохиогчийн урсгал нь эхлээд P(e) үйлдлийг гүйцэтгэх ба түүгээр буферийн санд хоосон буфер байгаа эсэхийг шалгадаг. P үйлдлийн семантикийн дагуу хэрэв семафор e нь 0-тэй тэнцүү бол (өөрөөр хэлбэл, одоогоор чөлөөт буфер байхгүй) зохиолчийн утас хүлээгдэж буй төлөвт орно. Хэрэв e-ийн утга эерэг тоо байвал чөлөөт буферийн тоог багасгаж, дараагийн чөлөөт буферт өгөгдөл бичиж, V(f) үйлдлээр эзлэгдсэн буферийн тоог нэмэгдүүлнэ. Уншигчийн хэлхээ нь ижил төстэй байдлаар ажилладаг бөгөөд ялгаа нь энэ нь бүрэн буфер байгаа эсэхийг шалгаж эхэлдэг бөгөөд өгөгдлийг уншсаны дараа чөлөөт буферийн тоог нэмэгдүүлдэг.

DIV_ADBLOCK860">

Семафорыг блоклох хувьсагч болгон ашиглаж болно. Дээр дурдсан жишээн дээр хуваалцсан санах ойтой ажиллах үед зөрчилдөөнийг арилгахын тулд бид буфер руу бичих, унших нь чухал хэсэг гэж үзэх болно. Бид хоёртын семафор b (Зураг 4.21) ашиглан харилцан хамааралгүй байдлыг хангах болно. Хоёр урсгал нь буфер байгаа эсэхийг шалгасны дараа чухал хэсгийн бэлэн байдлыг шалгах ёстой.

https://pandia.ru/text/78/239/images/image007_110.jpg" өргөн "495" өндөр "639 src=">

Цагаан будаа. 4.22.Програмыг гүйцэтгэх явцад түгжрэл үүсэх

ЖИЧ

Түгжрэлийг энгийн дарааллаас ялгах ёстой, гэхдээ хоёулаа нөөцийг хуваалцах үед үүсдэг бөгөөд гадаад төрхөөрөө ижил төстэй байдаг: хэлхээг түдгэлзүүлж, нөөц чөлөөлөгдөхийг хүлээж байна. Гэсэн хэдий ч дараалал нь ердийн үзэгдэл бөгөөд хүсэлт санамсаргүй байдлаар ирэх үед нөөцийн ашиглалт өндөр байгаагийн шинж тэмдэг юм. Одоогоор нөөц байхгүй үед дараалал гарч ирэх боловч хэсэг хугацааны дараа гарах бөгөөд энэ нь хэлхээг үргэлжлүүлэн ажиллуулах боломжийг олгоно. Нэрнээс нь харахад мухардмал байдал нь шийдэгдэх боломжгүй нөхцөл байдал юм. Түгжрэл үүсэх зайлшгүй нөхцөл бол thread-д нэгэн зэрэг хэд хэдэн нөөц хэрэгтэй байх явдал юм.

Харгалзан үзсэн жишээнүүдэд мухардлыг хоёр урсгалаар үүсгэсэн боловч илүү олон утаснууд бие биенээ хааж болно. Зураг дээр. Зураг 2.23-т Ri нөөцийн хэд хэдэн Tj урсгалын хооронд ийм хуваарилалтыг харуулсан бөгөөд энэ нь мухардмал үүсэхэд хүргэсэн. Сумнууд нь урсгалын нөөцийн шаардлагыг заана. Хатуу сум нь тухайн утас руу харгалзах нөөцийг хуваарилсан гэсэн үг бөгөөд тасархай сум нь утсыг шаардлагатай нөөцтэй холбосон боловч өөр урсгалтай байгаа тул хуваарилах боломжгүй байна. Жишээлбэл, T1 урсгалд ажлыг гүйцэтгэхийн тулд R1 ба R2 нөөц хэрэгтэй бөгөөд тэдгээрийн зөвхөн нэг нь R1 хуваарилагдсан бөгөөд R2 эх үүсвэр нь T2 урсгалд байдаг. Зурагт үзүүлсэн дөрвөн утаснуудын аль нь ч ажлаа үргэлжлүүлж чадахгүй, учир нь тэдэнд шаардлагатай бүх нөөц байхгүй.

Түгжрэлийн улмаас утаснууд эхлүүлсэн ажлаа дуусгах боломжгүй байгаа нь тооцоолох системийн ажиллагааг бууруулдаг. Тиймээс гацаанаас урьдчилан сэргийлэх асуудалд ихээхэн анхаарал хандуулдаг. Гацаа үүссэн тохиолдолд систем нь операторын операторыг эх үүсвэрийн түр зуурын боломжгүй байдлаас үүдэн гацааг таних, ердийн блокоос ялгах арга хэрэгслээр хангах ёстой. Эцэст нь, хэрэв түгжрэл оношлогдсон бол түгжрэлийг арилгах, тооцоолох хэвийн үйл явцыг сэргээх арга хэрэгсэл шаардлагатай болно.

Эзэмшигч" href="/text/category/vladeletc/" rel="bookmark">эзэмшигч, үүнийг дохиогүй төлөвт тохируулж, чухал хэсэгт ордог. Утас нь чухал өгөгдөлтэй ажиллаж дууссаны дараа "өгөгддөг. up” мутексийг дохионы төлөвт тохируулна. Одоогоор мутекс чөлөөтэй бөгөөд ямар ч урсгалд хамаарахгүй. Хэрэв ямар нэгэн урсгал гарахыг хүлээж байгаа бол энэ мутексийн дараагийн эзэмшигч болно. Үүний зэрэгцээ мутекс дохиогүй төлөвт ордог.

Үйл явдлын объект (энэ тохиолдолд "үйл явдал" гэдэг үгийг нарийн утгаар, тодорхой төрлийн синхрончлолын объектын тэмдэглэгээ болгон ашигладаг) ихэвчлэн өгөгдөлд хандахын тулд бус, харин зарим үйлдэл дууссаныг бусад хэлхээнд мэдэгдэхэд ашигладаг. Жишээлбэл, зарим програмын ажлыг нэг урсгал нь файлаас өгөгдлийг санах ойн буфер руу уншиж, бусад хэлхээнүүд энэ өгөгдлийг боловсруулдаг, дараа нь эхний хэлхээ нь өгөгдлийн шинэ хэсгийг уншдаг, бусад хэлхээнүүд нь ийм байдлаар зохион байгуулагдсан байг. дахин боловсруулах гэх мэт. Гүйцэтгэлийн эхэнд эхний хэлхээ нь үйл явдлын объектыг дохиогүй төлөвт тохируулдаг. Бусад бүх хэлхээнүүд Wait(X) руу залгасан бөгөөд X нь үйл явдлын заагч бөгөөд түр зогссон төлөвт байгаа бөгөөд тухайн үйл явдал гарахыг хүлээж байна. Буфер дүүрмэгц эхний хэлхээ нь Set(X) руу залгаж үйлдлийн системд мэдээлнэ. Үйлдлийн систем нь хүлээгдэж буй хэлхээнүүдийн дарааллыг сканнердаж, энэ үйл явдлыг хүлээж буй бүх хэлхээг идэвхжүүлдэг.

Дохио

ДохиоҮйлдлийн эх үүсвэр нь үйлдлийн систем эсвэл өөр даалгавар байж болзошгүй үйл явдалд хариу үйлдэл үзүүлэхийг даалгаварт зөвшөөрдөг. Дохио нь ажлыг тасалдуулах, урьдчилан тодорхойлсон үйлдлүүдийг гүйцэтгэх зэрэг орно. Дохио нь синхрон, өөрөөр хэлбэл процессын өөрийнх нь ажлын үр дүнд эсвэл өөр процессоор процесс руу илгээгдэж, өөрөөр хэлбэл асинхрон үүсгэж болно. Синхрон дохио нь ихэвчлэн процессорын тасалдлын системээс ирдэг бөгөөд тэгээр хуваагдах, хаягийн алдаа, санах ойн хамгаалалтын зөрчил гэх мэт техник хангамжаар хаагдсан процессын үйлдлүүдийг илэрхийлдэг.

Асинхрон дохионы жишээ бол терминалын дохио юм. Олон үйлдлийн системүүд нь гүйцэтгэлээс процессыг хурдан арилгах боломжийг олгодог. Үүнийг хийхийн тулд хэрэглэгч тодорхой товчлуурын хослолыг (Ctrl+C, Ctrl+Break) дарж, үүний үр дүнд OS дохио үүсгэж, идэвхтэй процесс руу илгээдэг. Сигнал нь процессын гүйцэтгэлийн үед ямар ч үед ирж болно (өөрөөр хэлбэл энэ нь асинхрон) бөгөөд энэ нь процессыг нэн даруй зогсоохыг шаарддаг. Энэ тохиолдолд дохионы хариу үйлдэл нь үйл явцыг болзолгүй дуусгах явдал юм.

Системд дохионы багцыг тодорхойлж болно. Сигналыг хүлээн авсан процессын програмын код нь үүнийг үл тоомсорлож эсвэл стандарт үйлдлээр хариу үйлдэл үзүүлэх (жишээлбэл, гарах) эсвэл програмын програмист тодорхойлсон тодорхой үйлдлүүдийг хийж болно. Сүүлчийн тохиолдолд програмын кодонд системийн тусгай дуудлагуудыг оруулах шаардлагатай бөгөөд үүний тусламжтайгаар үйлдлийн системд тодорхой дохио хүлээн авсны хариуд ямар процедурыг гүйцэтгэх ёстойг мэдээлдэг.

Дохио нь процессуудын хооронд болон процессууд болон хэрэглэгчид (терминалууд) хоорондын логик холбоог хангадаг. Дохио илгээх нь процессын тодорхойлогчийн талаар мэдлэг шаарддаг тул дохиогоор дамжуулан харилцан үйлчлэл нь зөвхөн бие биенийхээ тодорхойлогчийн талаар мэдээлэл авах боломжтой холбоотой процессуудын хооронд л боломжтой байдаг.

Тус бүр өөрийн гэсэн RAM-тай олон процессороос бүрдэх тархсан системд түгжих хувьсагч, семафор, дохио болон бусад ижил төстэй хуваалцсан санах ойд суурилсан функцууд тохиромжгүй байдаг. Ийм системд синхрончлолыг зөвхөн мессеж солилцох замаар хийж болно.

Процесс нь санах ойд ачаалагдсан програмын жишээ юм. Энэ жишээ нь гүйцэтгэх зааврын дараалал болох урсгалуудыг үүсгэж болно. Энэ нь ажиллаж байгаа процессууд биш, харин урсгалууд гэдгийг ойлгох нь чухал юм.

Түүнээс гадна аливаа процесс дор хаяж нэг урсгалтай байдаг. Энэ утсыг програмын үндсэн (гол) урсгал гэж нэрлэдэг.

Гүйцэтгэх физик процессоруудаас бараг үргэлж олон thread байдаг тул урсгалууд нь нэгэн зэрэг гүйцэтгэгддэггүй, харин эргээд (процессорын цагийг урсгалуудын хооронд хуваарилдаг). Гэхдээ тэдгээрийн хооронд шилжих нь маш олон удаа тохиолддог тул тэд зэрэгцээ ажиллаж байгаа мэт санагддаг.

Нөхцөл байдлаас хамааран утаснууд гурван төлөвт байж болно. Нэгдүгээрт, урсгал нь CPU-ийн цаг хуваарилагдсан үед ажиллах боломжтой, өөрөөр хэлбэл. үйл ажиллагааны төлөв байдалд байж болно. Хоёрдугаарт, энэ нь идэвхгүй байж магадгүй бөгөөд процессорыг хуваарилахыг хүлээж байна, өөрөөр хэлбэл. бэлэн байдалд байх. Гурав дахь, бас маш чухал төлөв байдаг - блоклох төлөв. Сэдвийг блоклох үед түүнд ямар ч цаг хугацаа огт хуваарилагдахгүй. Ихэвчлэн ямар нэгэн үйл явдлыг хүлээж байхад блок тавьдаг. Энэ үйл явдал тохиолдоход утас автоматаар блоклогдсон төлөвөөс бэлэн байдалд шилждэг. Жишээлбэл, хэрэв нэг утас тооцоолол хийж байгаа бол нөгөө нь үр дүнг дискэнд хадгалахыг хүлээх ёстой. Хоёр дахь нь "while(!isCalcFinished) continue;" гэх мэт давталтыг ашиглаж болох боловч энэ давталтыг гүйцэтгэх явцад процессор 100% завгүй байдаг (үүнийг идэвхтэй хүлээх гэж нэрлэдэг) гэдгийг практик дээр шалгахад хялбар байдаг. Түгжих механизм нь үнэлж баршгүй тусламж үзүүлдэг ийм мөчлөгүүдээс боломжтой бол зайлсхийх хэрэгтэй. Эхний хэлхээ нь уншиж дууссаныг илтгэх үйл явдлыг үүсгэх хүртэл хоёр дахь хэлхээ өөрийгөө хааж болно.

Windows үйлдлийн систем дэх хэлхээг синхрончлох

Windows нь урьдчилан сэргийлэх олон ажлыг хэрэгжүүлдэг - энэ нь систем ямар ч үед нэг урсгалын гүйцэтгэлийг тасалдуулж, хяналтыг нөгөө рүү шилжүүлэх боломжтой гэсэн үг юм. Өмнө нь Windows 3.1-д хамтын ажиллагаа гэж нэрлэгддэг зохион байгуулалтын аргыг ашигладаг байсан: систем нь хэлхээ өөрөө түүнд хяналтыг шилжүүлэх хүртэл хүлээдэг байсан тул нэг програм нь царцсан тохиолдолд компьютерийг дахин ачаалах шаардлагатай болдог.

Нэг процесст хамаарах бүх урсгалууд нь RAM хаягийн зай эсвэл нээлттэй файл гэх мэт нийтлэг нөөцийг хуваалцдаг. Эдгээр нөөцүүд нь бүхэл бүтэн процесст хамаарах тул түүний хэлхээ тус бүрт хамаарна. Тиймээс thread бүр эдгээр нөөцүүдтэй ямар ч хязгаарлалтгүйгээр ажиллах боломжтой. Гэхдээ... Хэрэв нэг хэлхээ зарим нэг хуваалцсан эх сурвалжтай ажиллаж дуусаагүй байгаа бөгөөд систем ижил нөөцийг ашиглан өөр урсгал руу шилжсэн бол эдгээр хэлхээний ажлын үр дүн төлөвлөснөөс тэс өөр байж болно. Ийм зөрчилдөөн нь янз бүрийн процесст хамаарах утаснуудын хооронд үүсч болно. Хоёр ба түүнээс дээш хэлхээ ямар нэгэн хуваалцсан эх сурвалжийг хуваалцах бүрд ийм асуудал гардаг.

Жишээ. Синхрончлолгүй хэлхээнүүд: Хэрэв та гаралтын урсгалыг түр зогсоовол (түр зогсоох) арын массивын популяци урсгалыг үргэлжлүүлэн ажиллуулна.

#оруулна #оруулна int a; HANDLE hThr; гарын үсэг зураагүй урт uThrID; хүчингүй Thread(void* pParams) ( int i, num = 0; while (1) ( for (i=0; i)<5; i++) a[i] = num; num++; } } int main(void) { hThr=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread,NULL,0,&uThrID); while(1) printf("%d %d %d %d %d\n", a, a, a, a, a); return 0; }

Ийм учраас хэлхээнд өөрсдийн ажлыг хуваалцсан нөөцтэй зохицуулах боломжийг олгох механизм хэрэгтэй байна. Энэ механизмыг thread синхрончлолын механизм гэж нэрлэдэг.

Энэ механизм нь үйлдлийн системийн объектуудын багц бөгөөд программчлалын дагуу бүтээгдэж, удирддаг, систем дэх бүх урсгалд нийтлэг байдаг (заримыг нь ижил процесст хамаарах thread-ууд хуваалцдаг), нөөцөд хандах хандалтыг зохицуулахад ашигладаг. Нөөцүүд нь хоёр ба түүнээс дээш урсгалаар хуваалцаж болох бүх зүйл байж болно - диск файл, порт, өгөгдлийн сангийн оруулга, GDI объект, тэр ч байтугай глобал програмын хувьсагч (ижил процесст хамаарах thread-ууд хандах боломжтой).

Хэд хэдэн синхрончлолын объектууд байдаг бөгөөд тэдгээрийн хамгийн чухал нь мутекс, чухал хэсэг, үйл явдал, семафор юм. Эдгээр объект бүр өөрийн гэсэн синхрончлолын аргыг хэрэгжүүлдэг. Мөн процессууд болон урсгалуудыг өөрөө синхрончлолын объект болгон ашиглаж болно (нэг урсгал нь өөр урсгал эсвэл процесс дуусахыг хүлээх үед); түүнчлэн файлууд, холбооны төхөөрөмж, консолын оролт, өөрчлөлтийн мэдэгдлүүд.

Аливаа синхрончлолын объект нь дохионы төлөвт байж болно. Объект бүрийн хувьд энэ төлөв өөр өөр утгатай. Threads нь объектын одоогийн төлөвийг шалгах ба/эсвэл энэ төлөвийн өөрчлөлтийг хүлээж, улмаар тэдний үйлдлийг зохицуулах боломжтой. Энэ нь утас нь синхрончлолын объектуудтай ажиллах үед (тэдгээрийг үүсгэх, төлөвийг өөрчлөх) систем нь энэ үйлдлийг дуусгах хүртэл түүний гүйцэтгэлийг тасалдуулахгүй гэдгийг баталгаажуулдаг. Тиймээс синхрончлолын объектуудтай хийсэн бүх эцсийн үйлдлүүд нь атомын (хуваагдах боломжгүй.

Синхрончлолын объектуудтай ажиллах

Нэг буюу өөр синхрончлолын объектыг үүсгэхийн тулд Create... төрлийн WinAPI тусгай функцийг дууддаг (жишээлбэл, CreateMutex). Энэ дуудлага нь энэ процесст хамаарах бүх урсгалд ашиглагдах объектын (HANDLE) бариулыг буцаана. Өөр процессоос синхрончлолын объект руу хандах боломжтой - энэ объект руу бариулыг залгамжлах, эсвэл объектыг нээх функц руу залгах замаар (Нээлттэй ...). Энэ дуудлагын дараа процесс нь дараа нь объекттой ажиллахад ашиглаж болох бариулыг хүлээн авах болно. Нэг процесст ашиглахаар төлөвлөөгүй л бол объектод нэр өгөх ёстой. Бүх объектын нэр өөр байх ёстой (өөр өөр төрлийн байсан ч). Жишээлбэл, та ижил нэртэй үйл явдал болон семафор үүсгэх боломжгүй.

Объектын одоо байгаа тодорхойлогчийг ашиглан түүний одоогийн төлөвийг тодорхойлж болно. Энэ нь гэж нэрлэгддэг зүйлийг ашиглан хийгддэг. хүлээгдэж буй функцууд. Хамгийн түгээмэл хэрэглэгддэг функц бол WaitForSingleObject юм. Энэ функц нь хоёр параметрийг авдаг бөгөөд эхнийх нь объектын бариул, хоёр дахь нь ms дэх хугацаа юм. Хэрэв объектод дохио өгвөл функц нь WAIT_OBJECT_0, хугацаа дууссан бол WAIT_TIMEOUT, хэрэв mutex объектыг эзэмшиж буй урсгалаас гарахаас өмнө чөлөөлөөгүй бол WAIT_ABANDONED-г буцаана. Хэрэв хугацаа нь тэг гэж заасан бол функц нь үр дүнг шууд буцаана, эс тэгвээс заасан хугацааг хүлээнэ. Хэрэв энэ хугацаа дуусахаас өмнө объектын төлөв дохио болж хувирвал функц WAIT_OBJECT_0-г буцаана, эс бөгөөс функц WAIT_TIMEOUT-г буцаана. Хэрэв INFINITE бэлгэдлийн тогтмолыг цаг гэж заасан бол тухайн объектын төлөв дохио болох хүртэл функц тодорхойгүй хугацаагаар хүлээх болно.

Маш чухал баримт бол хүлээх функцийг дуудах нь одоогийн хэлхээг блоклодог, i.e. Thread нь идэвхгүй байдалд байх үед түүнд CPU-ийн цаг хуваарилагддаггүй.

Чухал хэсгүүд

Чухал хэсгийн объект нь програмистад урсгал нь хуваалцсан нөөцөд ханддаг кодын хэсгийг тусгаарлаж, нөөцийг зэрэг ашиглахаас сэргийлдэг. Нөөцийг ашиглахын өмнө утас нь чухал хэсэгт ордог (EnterCriticalSection функцийг дууддаг). Хэрэв өөр ямар нэгэн хэлхээ ижил чухал хэсэгт орохыг оролдвол эхний хэлхээг LeaveCriticalSection гэж дуудаж хэсгээс гарах хүртэл түүний гүйцэтгэл түр зогсоно. Зөвхөн нэг процессын хэлхээнд ашигладаг. Чухал хэсэгт орох дарааллыг тодорхойлоогүй байна.

Мөн чухал хэсэг нь одоо байгаа эсэхийг шалгах TryEnterCriticalSection функц байдаг. Түүний тусламжтайгаар эх сурвалжид хандахыг хүлээж байгаа утсыг хааж болохгүй, гэхдээ зарим хэрэгтэй үйлдлүүдийг гүйцэтгэдэг.

Жишээ. Чухал хэсгүүдийг ашиглан хэлхээг синхрончлох.

#оруулна #оруулна CRITICAL_SECTION cs; int a; HANDLE hThr; гарын үсэг зураагүй урт uThrID; void Thread(void* pParams) ( int i, num = 0; while (1) ( EnterCriticalSection(&cs); for (i=0; i)<5; i++) a[i] = num; num++; LeaveCriticalSection(&cs); } } int main(void) { InitializeCriticalSection(&cs); hThr=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread,NULL,0,&uThrID); while(1) { EnterCriticalSection(&cs); printf("%d %d %d %d %d\n", a, a, a, a, a); LeaveCriticalSection(&cs); } return 0; }

Бие биенээ үгүйсгэх

Харилцан тусгаарлах объектууд (mutexes, mutex - MUTual Exclusion-аас) нь хуваалцсан нөөцөд хандах хандалтыг харилцан хориглох ажлыг зохицуулах боломжийг танд олгоно. Объектийн дохионы төлөв (өөрөөр хэлбэл "тогтоосон" төлөв) нь тухайн объект ямар ч урсгалд хамаарахгүй бөгөөд "барьж авах" боломжтой цаг хугацааны цэгтэй тохирч байна. Эсрэгээр, "дахин тохируулах" (дохиогүй) төлөв нь энэ объектыг аль хэдийн эзэмшсэн хэсэгтэй тохирч байна. Объектыг эзэмшдэг урсгал нь түүнийг суллах үед объект руу хандах эрхийг олгоно.

Хоёр (эсвэл түүнээс дээш) хэлхээ нь CreateMutex функцийг дуудаж ижил нэртэй мутекс үүсгэж болно. Эхний хэлхээ нь үнэндээ мутекс үүсгэдэг бөгөөд дараагийнх нь аль хэдийн байгаа объектын зохицуулалтыг авдаг. Энэ нь олон thread-д нэг мутекстэй ажиллах боломжийг олгож, программистыг яг хэн мутекс үүсгэсэн талаар санаа зовохоос чөлөөлдөг. Хэрэв энэ аргыг хэрэглэвэл bInitialOwner тугийг FALSE гэж тохируулахыг зөвлөж байна, эс тэгвээс mutex-ийн жинхэнэ үүсгэгчийг тодорхойлоход зарим хүндрэл гарах болно.

Олон урсгалууд нь ижил мутекстэй ажиллах боломжтой бөгөөд энэ нь процесс хоорондын харилцаа холбоог бий болгодог. Энэ хандлагын дараах механизмуудыг ашиглаж болно.

  • CreateProcess функцийг ашиглан үүсгэсэн хүүхэд процесс нь CreateMutex функцээр мутекс үүсгэх үед lpMutexAttributes параметрийг зааж өгсөн бол мутекс бариулыг өвлөн авах боломжтой.
  • Thread нь DuplicateHandle функцийг ашиглан одоо байгаа мутексийн хуулбарыг авах боломжтой.
  • OpenMutex эсвэл CreateMutex функцуудыг дуудах үед урсгал нь одоо байгаа мутексийн нэрийг зааж өгч болно.

Харилцан үл хамаарах зүйлийг одоогийн хэлхээнд хамаарах гэж зарлахын тулд та хүлээх функцүүдийн аль нэгийг дуудах ёстой. Объектыг эзэмшдэг утас нь хүссэн хэмжээгээрээ дахин авах боломжтой (энэ нь өөрөө өөрийгөө түгжихэд хүргэхгүй), гэхдээ ReleaseMutex функцийг ашиглан ижил тооны удаа суллах ёстой.

Нэг процессын хэлхээг синхрончлохын тулд чухал хэсгүүдийг ашиглах нь илүү үр дүнтэй байдаг.

Жишээ. Мутекс ашиглан урсгалуудыг синхрончлох.

#оруулна #оруулна HANDLE hMutex; int a; HANDLE hThr; гарын үсэг зураагүй урт uThrID; void Thread(void* pParams) ( int i, num = 0; while (1) ( WaitForSingleObject(hMutex, INFINITE); for (i=0; i)<5; i++) a[i] = num; num++; ReleaseMutex(hMutex); } } int main(void) { hMutex=CreateMutex(NULL, FALSE, NULL); hThr=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread,NULL,0,&uThrID); while(1) { WaitForSingleObject(hMutex, INFINITE); printf("%d %d %d %d %d\n", a, a, a, a, a); ReleaseMutex(hMutex); } return 0; }

Үйл явдал

Үйл явдлын объектууд нь үйл явдал болсон тухай хүлээгдэж буй хэлхээнд мэдэгдэхэд ашиглагддаг. Гараар болон автоматаар дахин тохируулах гэсэн хоёр төрлийн үйл явдал байдаг. Гараар дахин тохируулах нь ResetEvent функцээр хийгддэг. Гараар дахин тохируулах үйл явдлууд нь олон хэлхээнд нэгэн зэрэг мэдэгдэхэд ашиглагддаг. Автоматаар дахин тохируулах үйлдлийг ашиглах үед зөвхөн нэг хүлээгдэж буй хэлхээ нь мэдэгдлийг хүлээн авч, үргэлжлүүлэн гүйцэтгэх болно, үлдсэн хэсэг нь үргэлжлүүлэн хүлээх болно.

CreateEvent функц нь үйл явдлын объектыг үүсгэдэг, SetEvent - үйл явдлыг дохионы төлөвт тохируулдаг, ResetEvent - үйл явдлыг дахин тохируулдаг. PulseEvent функц нь үйл явдлыг тохируулдаг бөгөөд энэ үйл явдлыг хүлээж буй хэлхээг дахин эхлүүлсний дараа (бүгдийг гараар дахин тохируулж, зөвхөн нэгийг автоматаар дахин тохируулах боломжтой) дахин тохируулдаг. Хэрэв хүлээгдэж буй хэлхээ байхгүй бол PulseEvent үйл явдлыг дахин тохируулна.

Жишээ. Үйл явдлыг ашиглан хэлхээг синхрончлох.

#оруулна #оруулна hEvent1, hEvent2-г ХЭРЭГЛЭХ; int a; HANDLE hThr; гарын үсэг зураагүй урт uThrID; void Thread(void* pParams) ( int i, num = 0; while (1) ( WaitForSingleObject(hEvent2, INFINITE); for (i=0; i)<5; i++) a[i] = num; num++; SetEvent(hEvent1); } } int main(void) { hEvent1=CreateEvent(NULL, FALSE, TRUE, NULL); hEvent2=CreateEvent(NULL, FALSE, FALSE, NULL); hThr=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread,NULL,0,&uThrID); while(1) { WaitForSingleObject(hEvent1, INFINITE); printf("%d %d %d %d %d\n", a, a, a, a, a); SetEvent(hEvent2); } return 0; }

Семафорууд

Семафор объект нь үнэндээ тоолууртай mutex объект юм. Энэ объект нь өөрийгөө тодорхой тооны утсаар "барьж авах" боломжийг олгодог. Үүний дараа семафорыг өмнө нь "барьж авсан" утаснуудын аль нэг нь үүнийг гаргах хүртэл "барьж авах" боломжгүй болно. Семафорыг нөөцтэй нэгэн зэрэг ажиллах урсгалын тоог хязгаарлахад ашигладаг. Эхлүүлэх явцад хамгийн их урсгалын тоог объект руу шилжүүлдэг бөгөөд "барьж авах" бүрийн дараа семафорын тоолуур буурдаг. Сигналын төлөв нь тэгээс их тоологч утгатай тохирч байна. Тоолуур тэг байх үед семафорыг суулгаагүй (дахин тохируулсан) гэж үзнэ.

CreateSemaphore функц нь түүний боломжит хамгийн их анхны утгыг харуулсан семафор объектыг үүсгэдэг, OpenSemaphore - одоо байгаа семафорын тодорхойлогчийг буцаана, семафорыг хүлээх функцийг ашиглан буулгаж, семафорын утгыг нэгээр бууруулна, ReleaseSemaphore - семафорыг семафортой хамт гаргадаг. параметрийн дугаарт заасан утгаар нэмэгдсэн утга.

Жишээ. Семафор ашиглан хэлхээг синхрончлох.

#оруулна #оруулна HANDLE hSem; int a; HANDLE hThr; гарын үсэг зураагүй урт uThrID; void Thread(void* pParams) ( int i, num = 0; while (1) ( WaitForSingleObject(hSem, INFINITE); for (i=0; i)<5; i++) a[i] = num; num++; ReleaseSemaphore(hSem, 1, NULL); } } int main(void) { hSem=CreateSemaphore(NULL, 1, 1, "MySemaphore1"); hThr=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread,NULL,0,&uThrID); while(1) { WaitForSingleObject(hSem, INFINITE); printf("%d %d %d %d %d\n", a, a, a, a, a); ReleaseSemaphore(hSem, 1, NULL); } return 0; }

Хувьсагчдад хамгаалагдсан хандалт

Синхрончлолын талаар санаа зовохгүйгээр бүх урсгалын глобал хувьсагчтай ажиллах боломжийг олгодог хэд хэдэн функц байдаг. Эдгээр функцууд үүнийг өөрсдөө хянадаг - тэдгээрийн гүйцэтгэл нь атом юм. Эдгээр функцууд нь InterlockedIncrement, InterlockedDecrement, InterlockedExchange, InterlockedExchangeAdd болон InterlockedCompareExchange юм. Жишээлбэл, InterlockedIncrement функц нь 32 битийн хувьсагчийн утгыг атомаар нэгээр нэмэгдүүлдэг бөгөөд энэ нь янз бүрийн тоолуурт ашиглахад тохиромжтой.

WIN32 API-ийн бүх функцүүдийн зорилго, ашиглалт, синтаксийн талаар бүрэн мэдээлэл авахын тулд Borland Delphi эсвэл CBuilder програмчлалын орчинд багтсан MS SDK тусламжийн систем, мөн Visual C програмчлалын системийн нэг хэсэг болгон нийлүүлсэн MSDN-ийг ашиглах ёстой.

Threads нь хэд хэдэн төлөвийн аль нэгэнд байж болно:

    Бэлэн(бэлэн) - гүйцэтгэлийг хүлээж буй утаснуудын санд байрладаг;

    Гүйж байна(гүйцэтгэх) - процессор дээр ажиллаж байгаа;

    Хүлээж байна(хүлээж байна), мөн сул зогссон эсвэл түр зогссон гэж нэрлэдэг, түр зогссон - хүлээгдэж буй төлөвт байгаа бөгөөд энэ нь утас ажиллаж эхлэх (Ажиллаж байгаа төлөв) эсвэл төлөвт орох үед дуусдаг. Бэлэн;

    Дууссан (дуусгах) - бүх урсгалын командын гүйцэтгэл дууссан. Үүнийг дараа нь устгаж болно. Хэрэв дамжуулалтыг устгаагүй бол систем дараа нь ашиглахын тулд анхны төлөвт нь оруулж болно.

Утас синхрончлол

Ажиллаж буй thread нь ихэвчлэн ямар нэгэн байдлаар харилцах шаардлагатай болдог. Жишээлбэл, хэрэв олон хэлхээ нь зарим глобал өгөгдөлд хандахыг оролдож байгаа бол хэлхээ бүр өгөгдлийг өөр thread-ээр өөрчлөхөөс хамгаалах шаардлагатай. Заримдаа нэг хэлхээ өөр хэлхээ хэзээ даалгавар гүйцэтгэхийг мэдэх шаардлагатай болдог. Ийм харилцан үйлчлэл нь ижил ба өөр процессуудын утаснуудын хооронд заавал байх ёстой.

Утас синхрончлол ( утас синхрончлол) нь утаснуудын харилцан үйлчлэл, харилцан холболтын үйл явцыг илэрхийлдэг ерөнхий нэр томъёо юм. Сэдвүүдийг синхрончлоход үйлдлийн систем өөрөө зуучлагчийн үүрэг гүйцэтгэхийг шаарддаг гэдгийг анхаарна уу. Түүний оролцоогүйгээр утаснууд хоорондоо харилцан үйлчлэх боломжгүй.

Win32 дээр хэлхээг синхрончлох хэд хэдэн арга байдаг. Тодорхой нөхцөлд нэг аргыг нөгөөгөөсөө илүүд үздэг. Эдгээр аргуудыг хурдан харцгаая.

Чухал хэсгүүд

Сэдвүүдийг синхрончлох нэг арга бол чухал хэсгүүдийг ашиглах явдал юм. Энэ бол Windows цөмийг шаарддаггүй урсгалын синхрончлолын цорын ганц арга юм. (Чухал хэсэг нь цөмийн объект биш юм.) Гэхдээ энэ аргыг зөвхөн нэг процессын хэлхээг синхрончлоход ашиглаж болно.

Чухал хэсэг нь нэг удаад зөвхөн нэг урсгалаар гүйцэтгэх кодын хэсэг юм. Хэрэв массивыг эхлүүлэхэд ашигласан кодыг чухал хэсэгт байрлуулсан бол эхний хэлхээ үүнийг гүйцэтгэж дуусах хүртэл бусад хэлхээнүүд кодын тэр хэсгийг оруулах боломжгүй болно.

Чухал хэсгийг ашиглахын өмнө та Win32 API процедурыг (Delphi-д) дараах байдлаар тодорхойлсон InitializeCriticalSection() ашиглан эхлүүлэх хэрэгтэй.

журам InitializeCriticalSection(var IpCriticalSection: TRTLCriticalSection); stdcall;

IpCriticalSection параметр нь лавлагаагаар дамжуулагдсан TRTLCriticalSection төрлийн бичлэг юм. TRTLCriticalSection оруулгын яг тодорхой тодорхойлолт нь тийм ч чухал биш, учир нь та түүний агуулгыг харах шаардлагагүй болно. Таны хийх ёстой зүйл бол IpCtitical Section параметрт эхлээгүй оруулгыг дамжуулах бөгөөд энэ оруулгыг процедурын дагуу нэн даруй бөглөнө.

Програмын оруулгыг бөглөсний дараа EnterCriticalSection() болон LeaveCriticalSection() функцүүдийн дуудлагын хооронд текстийн зарим хэсгийг байрлуулснаар чухал хэсгийг үүсгэж болно. Эдгээр процедурыг дараах байдлаар тодорхойлно.

процедур EnterCriticalSection(var IpCriticalSection: TRTLCriticalSection); stdcall;

журам LeaveCriticalSection(var IpCriticalSection: TRTLCriticalSection); stdcall;

Эдгээр процедурт дамжуулагдсан IpCriticalSection параметр нь InitializeCriticalSection() процедурын үүсгэсэн оруулгаас өөр зүйл биш юм.

Чиг үүрэг EnterCriticalSectionөгөгдсөн эгзэгтэй хэсгийн объекттой холбоотой програмынхаа чухал хэсгийг өөр хэлхээ аль хэдийн гүйцэтгэж байгаа эсэхийг шалгана. Үгүй бол thread нь өөрийн чухал кодыг гүйцэтгэх зөвшөөрөл авдаг, эс тэгвээс үүнийг хийхээс сэргийлдэггүй. Хэрэв тийм бол хүсэлт гаргаж буй хэлхээг хүлээлтийн байдалд оруулж, хүсэлтийн талаар тэмдэглэл хийнэ. Бичлэг үүсгэх шаардлагатай тул чухал хэсгийн объект нь өгөгдлийн бүтэц юм.

Хэзээ функц CriticalSection-г орхиТухайн эгзэгтэй хэсгийн объекттой холбоотой кодын эгзэгтэй хэсгийг гүйцэтгэх зөвшөөрөлтэй урсгалаар дуудагдсан тохиолдолд систем дараалалд тухайн объектыг гаргахыг хүлээж байгаа өөр хэлхээ байгаа эсэхийг шалгах боломжтой. Дараа нь систем нь хүлээгдэж буй утсыг хүлээгдэж буй байдлаас нь салгах боломжтой бөгөөд энэ нь ажлаа үргэлжлүүлэх болно (түүнд хуваарилагдсан цагийн хуваарийн дагуу).

Та TRTLCriticalSection бичлэгтэй ажиллаж дууссаны дараа дараах байдлаар тодорхойлогдсон DeleteCriticalSection() процедурыг дуудаж үүнийг чөлөөлөх ёстой.

DeleteCriticalSection(var IpCriticalSection: TRTLCriticalSection); stdcall;

Заримдаа олон урсгалтай эсвэл процессуудтай ажиллахад шаардлагатай болдог гүйцэтгэлийг синхрончлохтэдгээрийн хоёр ба түүнээс дээш. Үүний шалтгаан нь ихэвчлэн хоёр ба түүнээс дээш хэлхээ нь хуваалцсан эх сурвалжид хандах шаардлагатай болдог үнэхээролон хэлхээнд нэгэн зэрэг өгөх боломжгүй. Хуваалцсан нөөц гэдэг нь олон ажил гүйцэтгэх замаар нэгэн зэрэг хандах боломжтой нөөц юм.

Синхрончлолын процессыг хангах механизмыг нэрлэдэг хандалтын хязгаарлалт.Үүний хэрэгцээ нь нэг хэлхээ өөр урсгалаар үүсгэгдсэн үйл явдлыг хүлээж байгаа тохиолдолд үүсдэг. Мэдээжийн хэрэг, үйл явдал болохоос өмнө эхний хэлхээг түр зогсоох арга зам байх ёстой. Үүний дараа утас нь гүйцэтгэлээ үргэлжлүүлэх ёстой.

Даалгавар байж болох хоёр ерөнхий төлөв байдаг. Нэгдүгээрт, даалгавар хийж болно гүйцэтгэнэ(эсвэл CPU-ийн эх сурвалжид хандсан даруйд гүйцэтгэхэд бэлэн байх). Хоёрдугаарт, даалгавар байж болно блоклосон.Энэ тохиолдолд шаардлагатай нөөцийг чөлөөлөх эсвэл тодорхой үйл явдал гарах хүртэл түүний гүйцэтгэлийг түр зогсооно.

Windows нь хуваалцсан нөөцөд хандах хандалтыг тодорхой аргаар хязгаарлах боломжийг олгодог тусгай үйлчилгээтэй, учир нь үйлдлийн системийн тусламжгүйгээр тусдаа процесс эсвэл урсгал нь нөөцөд дангаар хандах боломжтой эсэхийг өөрөө тодорхойлж чадахгүй. Windows үйлдлийн систем нь нэг тасралтгүй ажиллагааны явцад нөөцийн хандалтын тугийг шалгаж, боломжтой бол тохируулах процедурыг агуулдаг. Үйлдлийн систем хөгжүүлэгчдийн хэлээр энэ үйлдлийг гэж нэрлэдэг шалгах, суурилуулах ажиллагаа. Синхрончлолыг хангах, нөөцөд хандах хандалтыг хянахад ашигладаг тугуудыг нэрлэдэг семафорууд(семафор). Win32 API нь семафор болон бусад синхрончлолын объектуудад дэмжлэг үзүүлдэг. MFC номын сан нь эдгээр объектуудад зориулсан дэмжлэгийг агуулдаг.

Синхрончлолын объектууд болон mfc ангиуд

Win32 интерфэйс нь дөрвөн төрлийн синхрончлолын объектыг дэмждэг бөгөөд тэдгээр нь бүгд ямар нэгэн байдлаар семафорын тухай ойлголт дээр суурилдаг.

Эхний төрлийн объект нь семафор өөрөө юм сонгодог (стандарт) семафор. Энэ нь хязгаарлагдмал тооны процессууд болон урсгалуудыг нэг нөөцөд хандах боломжийг олгодог. Энэ тохиолдолд нөөцөд хандах хандалт бүрэн хязгаарлагдмал (нэг бөгөөд зөвхөн нэг урсгал эсвэл процесс нь тодорхой хугацаанд нөөцөд хандах боломжтой), эсвэл зөвхөн цөөн тооны хэлхээ, процессууд нэгэн зэрэг хандалт авдаг. Семафорыг даалгаварт хуваарилах үед үнэ цэнэ нь буурч, семафорыг гаргах үед нэмэгддэг тоолуур ашиглан семафоруудыг хэрэгжүүлдэг.

Хоёр дахь төрлийн синхрончлолын объектууд онцгой (мутекс) семафор. Энэ нь нөөцөд хандах хандалтыг бүрэн хязгаарлах зорилготой бөгөөд ингэснээр зөвхөн нэг процесс эсвэл урсгал нь тухайн нөөцөд ямар ч үед хандах боломжтой. Үнэн хэрэгтээ энэ бол семафорын тусгай төрөл юм.

Гурав дахь төрлийн синхрончлолын объектууд үйл явдал, эсвэл үйл явдлын объект.Энэ нь тухайн нөөцийг ашиглах боломжтой гэж зарлах хүртэл өөр процесс эсвэл урсгалд хандах хандалтыг хаахад ашиглагддаг. Тиймээс энэ объект нь шаардлагатай үйл явдал дууссаныг дохио өгдөг.

Дөрөв дэх төрлийн синхрончлолын объектыг ашигласнаар та програмын кодын тодорхой хэсгийг хэд хэдэн урсгалаар нэгэн зэрэг гүйцэтгэхийг хориглож болно. Үүнийг хийхийн тулд эдгээр газруудыг зарлах ёстой чухал хэсэг. Нэг хэлхээ энэ хэсэгт ороход эхний хэлхээ хэсэгээс гарах хүртэл бусад хэлхээнүүд ижил зүйлийг хийхийг хориглоно.

Чухал хэсгүүд нь бусад төрлийн синхрончлолын объектуудаас ялгаатай нь зөвхөн ижил процессын хүрээнд хэлхээг синхрончлоход ашиглагддаг. Бусад төрлийн объектуудыг процесс доторх урсгалуудыг синхрончлох эсвэл процессуудыг синхрончлоход ашиглаж болно.

MFC-д Win32 интерфейсээр хангагдсан синхрончлолын механизмыг CSyncObject ангиас авсан дараах ангиуд дэмждэг.

    CCriticalSection- чухал хэсгийг хэрэгжүүлдэг.

    CEvent- үйл явдлын объектыг хэрэгжүүлдэг

    CMutex- онцгой семафорыг хэрэгжүүлдэг.

    CSemaphore- сонгодог семафорыг хэрэгжүүлдэг.

Эдгээр ангиудаас гадна MFC нь хоёр туслах синхрончлолын ангиллыг тодорхойлдог: CSingleLockТэгээд CMultiLock. Эдгээр нь синхрончлолын объект руу хандах хандалтыг хянадаг бөгөөд ийм объектыг хангах, гаргахад ашигладаг аргуудыг агуулдаг. Анги CSingleLockнэг синхрончлолын объект болон анги руу хандах хандалтыг хянадаг CMultiLock- хэд хэдэн объект руу. Дараах зүйлд бид зөвхөн ангиудыг авч үзэх болно CSingleLock.

Аливаа синхрончлолын объект үүсгэгдсэний дараа түүнд хандах хандалтыг класс ашиглан удирдаж болно CSingleLock. Үүнийг хийхийн тулд та эхлээд төрлийн объект үүсгэх хэрэгтэй CSingleLockүүсгэгчийг ашиглан:

CSingleLock(CSyncObject* pObject, BOOL bInitialLock = FALSE);

Эхний параметр нь заагчийг синхрончлолын объект руу дамжуулдаг, жишээ нь семафор. Хоёрдахь параметрийн утга нь бүтээгч энэ объект руу хандахыг оролдох эсэхийг тодорхойлдог. Хэрэв энэ параметр нь тэг биш бол хандалтыг авах болно, эс тэгвээс хандах оролдлого хийхгүй. Хэрэв хандалт олгогдсон бол ангийн объектыг үүсгэсэн хэлхээ болно CSingleLock, харгалзах синхрончлолын объектыг аргаар гаргах хүртэл зогсох болно Түгжээг тайлаханги CSingleLock.

CSingleLock төрлийн объектыг үүсгэх үед pObject-ийн зааж өгсөн объект руу хандах хандалтыг хоёр функцээр удирдаж болно: ТүгжихТэгээд Түгжээг тайлаханги CSingleLock.

Арга Түгжихнь объект руу синхрончлолын объект руу хандахад зориулагдсан. Үүнийг дуудсан хэлхээ нь арга дуусах хүртэл, өөрөөр хэлбэл нөөцөд хандах хүртэл түр зогсдог. Параметрийн утга нь функц шаардлагатай объект руу нэвтрэхийг хэр удаан хүлээхийг тодорхойлдог. Арга амжилттай дуусах бүрд синхрончлолын объекттой холбоотой тоолуурын утга нэгээр буурдаг.

Арга Түгжээг тайлахСинхрончлолын объектыг гаргаж, бусад хэлхээнд нөөцийг ашиглах боломжийг олгоно. Аргын эхний хувилбарт энэ объекттой холбоотой тоолуурын утгыг нэгээр нэмэгдүүлсэн. Хоёрдахь хувилбарт эхний параметр нь энэ утгыг хэр их хэмжээгээр нэмэгдүүлэхийг тодорхойлдог. Хоёрдахь параметр нь өмнөх тоологч утгыг бичих хувьсагчийг заана.

Ангитай ажиллахдаа CSingleLockНөөцөд хандах хандалтыг хянах ерөнхий журам нь:

    CSyncObj төрлийн объект үүсгэх (жишээлбэл, семафор) нөөцөд хандах хандалтыг хянах;

    үүсгэсэн синхрончлолын объектыг ашиглан CSingleLock төрлийн объект үүсгэх;

    нөөцөд хандахын тулд Lock аргыг дуудах;

    нөөцөд хандах;

    Нөөцийг гаргахын тулд Unlock аргыг дууд.

Дараах зүйлд семафор болон үйл явдлын объектыг хэрхэн үүсгэх, ашиглах талаар тайлбарласан болно. Эдгээр ойлголтыг ойлгосны дараа та өөр хоёр төрлийн синхрончлолын объектыг хялбархан сурч, ашиглаж болно: чухал хэсгүүд ба мутексууд.

Сайн уу? Өнөөдөр бид олон урсгалтай програмчлалын онцлогуудыг үргэлжлүүлэн авч үзэж, урсгалын синхрончлолын талаар ярих болно.

"Синхрончлол" гэж юу вэ? Програмчлалын хүрээнээс гадна энэ нь хоёр төхөөрөмж эсвэл програм хамтран ажиллах боломжийг олгодог зарим төрлийн тохиргоог хэлнэ. Жишээлбэл, ухаалаг гар утас, компьютерийг Google данстай, вэбсайт дахь хувийн дансаа нийгмийн сүлжээн дэх дансуудтай синхрончилж, тэдгээрийг ашиглан нэвтрэх боломжтой. Thread синхрончлол нь ижил утгатай: энэ нь утаснууд хоорондоо хэрхэн харьцаж байгааг тохируулах явдал юм. Өмнөх лекцүүдэд бидний утаснууд бие биенээсээ тусдаа ажиллаж, амьдарч байсан. Нэг нь ямар нэг зүйлийг тоолж, хоёр дахь нь унтаж, гурав дахь нь консол дээр ямар нэг зүйлийг харуулж байсан ч тэд хоорондоо харьцдаггүй. Бодит хөтөлбөрт ийм нөхцөл байдал ховор байдаг. Хэд хэдэн хэлхээ нь ижил өгөгдөлтэй идэвхтэй ажиллаж, доторх ямар нэг зүйлийг өөрчлөх боломжтой. Энэ нь асуудал үүсгэдэг. Олон хэлхээнүүд ижил байршилд текст бичиж байна гэж төсөөлөөд үз дээ, жишээ нь, текст файл эсвэл консол. Энэ тохиолдолд энэ файл эсвэл консол нь хуваалцсан нөөц болно. Threads нь бие биенийхээ оршин тогтнох талаар мэддэггүй тул тэд зүгээр л хэлхээний хуваарьлагчийн хуваарилсан хугацаанд удирдаж чадах бүх зүйлээ бичдэг. Саяхны нэг лекц дээр бид энэ нь юунд хүргэх тухай жишээтэй байсан, үүнийг санацгаая: Шалтгаан нь утаснууд нь хоорондоо уялдаа холбоотой үйлдлүүдгүйгээр хуваалцсан нөөц, консолтой ажилладаг байсантай холбоотой юм. Хэрэв thread төлөвлөгч Thread-1-д цаг хуваарилсан бол тэр даруй бүх зүйлийг консол руу бичнэ. Бусад сэдвүүд аль хэдийн бичиж чадсан эсвэл бичиж амжаагүй байгаа нь чухал биш. Таны харж байгаагаар үр дүн нь аймшигтай юм. Тиймээс олон урсгалтай програмчлалд тусгай ойлголтыг нэвтрүүлсэн мутекс (Англи хэлнээс "mutex", "харилцан гадуурхах" - "харилцан гадуурхах"). Mutex даалгавар- тодорхой цагт зөвхөн нэг урсгал нь объект руу хандах боломжтой байх механизмыг хангана. Хэрэв Thread-1 нь А объектын мутексийг олж авсан бол бусад thread-ууд түүнд ямар нэгэн зүйлийг өөрчлөх эрхгүй болно. А объектын мутекс гарах хүртэл үлдсэн утаснууд хүлээхээс өөр аргагүй болно. Бодит жишээ: Та болон бусад 10 танихгүй хүн сургалтад оролцож байна гэж төсөөлөөд үз дээ. Та ээлжлэн санал бодлоо илэрхийлж, ямар нэг зүйлийг хэлэлцэх хэрэгтэй. Гэхдээ та анх удаа уулзаж байгаа тул бие биенээ байнга тасалдуулахгүйн тулд "ярих бөмбөг" дүрмийг ашигладаг: зөвхөн нэг хүн л ярьж чадна - бөмбөгтэй хүн л ярьж чадна. түүний гар. Ингэснээр хэлэлцүүлэг хангалттай, үр дүнтэй болж хувирна. Тиймээс, мутекс бол үндсэндээ ийм бөмбөг юм. Хэрэв объектын мутекс нь нэг урсгалын гарт байвал бусад урсгалууд объект руу хандах боломжгүй болно. Мутекс үүсгэхийн тулд та юу ч хийх шаардлагагүй: энэ нь Object ангид аль хэдийн суурилагдсан бөгөөд энэ нь Java дахь бүх объект нэгтэй гэсэн үг юм.

Синхрончлогдсон оператор хэрхэн ажилладаг

Шинэ түлхүүр үгтэй танилцацгаая - синхрончлогдсон. Энэ нь бидний кодын тодорхой хэсгийг тэмдэглэдэг. Хэрэв кодын блокыг синхрончлогдсон түлхүүр үгээр тэмдэглэсэн бол энэ нь блокыг нэг удаад зөвхөн нэг урсгалаар гүйцэтгэх боломжтой гэсэн үг юм. Синхрончлолыг янз бүрийн аргаар хийж болно. Жишээлбэл, бүхэл бүтэн синхрончлогдсон аргыг үүсгэнэ үү: public synchronized void doSomething() () //... аргын логик) Эсвэл зарим объект дээр синхрончлол хийгддэг кодын блок бичнэ үү: public class Main ( private Object obj = new Object () ; public void doSomething () ( synchronized (obj) ( ) ) ) Утга нь энгийн. Хэрэв нэг хэлхээ нь синхрончлогдсон үгээр тэмдэглэгдсэн кодын блок руу орвол тэр даруй объектын мутексийг олж авах ба ижил блок эсвэл арга руу орохыг оролдсон бусад бүх хэлхээ нь өмнөх хэлхээ ажлаа дуусгаж, утсыг гаргах хүртэл хүлээхээс өөр аргагүй болно. хянах. Дашрамд хэлэхэд! Хичээлийн лекц дээр та синхрончлолын жишээг аль хэдийн харсан, гэхдээ тэдгээр нь өөр харагдаж байсан: public void swap () ( синхрончлогдсон (энэ) ( //... аргын логик) ) Энэ сэдэв нь таны хувьд шинэ бөгөөд мэдээжийн хэрэг эхлээд синтакстай андуурагдах болно. Тиймээс дараа нь бичих аргад төөрөхгүйн тулд тэр даруй санаж байгаарай. Эдгээр бичих хоёр арга нь ижил утгатай: нийтийн хүчингүй своп () ( синхрончлогдсон (энэ) ( //... аргын логик) ) public synchronized void swap () ( ) ) Эхний тохиолдолд та аргыг оруулсны дараа шууд синхрончлогдсон блок кодын үүсгэнэ. Энэ нь энэ объект, өөрөөр хэлбэл одоогийн объектоор синхрончлогддог. Хоёрдахь жишээнд та синхрончлогдсон үгийг бүхэл бүтэн аргын дагуу оруулав. Синхрончлол хийгдэж буй объектыг тодорхой зааж өгөх шаардлагагүй болсон. Бүхэл бүтэн аргыг үгээр тэмдэглэсний дараа энэ аргыг ангийн бүх объектод автоматаар синхрончлох болно. Аль арга нь илүү дээр вэ гэдгийг хэлэлцэх хэрэггүй. Одоохондоо өөрт хамгийн их таалагдсан зүйлээ сонгоорой :) Хамгийн гол нь санаж байгаарай: доторх бүх логикийг нэг урсгалаар нэгэн зэрэг гүйцэтгэсэн тохиолдолд л синхрончлогдсон аргыг зарлаж болно. Жишээлбэл, энэ тохиолдолд doSomething() аргыг синхрончлоход алдаа гарах болно: public class Main ( private Object obj = new Object () ; public void doSomething () () //... зарим логик бүх хэлхээнд боломжтойсинхрончлогдсон (объект) ( //зөвхөн нэг хэлхээнд ашиглах боломжтой логик) ) ) Таны харж байгаагаар аргын хэсэг нь синхрончлол хийх шаардлагагүй логикийг агуулдаг. Тэнд байгаа кодыг хэд хэдэн урсгалаар нэгэн зэрэг гүйцэтгэх боломжтой бөгөөд бүх чухал газруудыг тусдаа синхрончлогдсон блокт хуваарилдаг. Бас нэг хором. Лекцээс нэрээ солих жишээг микроскопоор харцгаая: public void swap () ( синхрончлогдсон (энэ) ( //... аргын логик } } Анхаар: Үүнийг ашиглан синхрончлолыг хийдэг. Энэ нь тодорхой MyClass объект дээр гэсэн үг юм. Бидэнд 2 урсгал (Thread-1 ба Thread-2) болон MyClass myClass нэг л объект байна гэж төсөөлөөд үз дээ. Энэ тохиолдолд Thread-1 нь myClass.swap() аргыг дуудвал объектын мутекс завгүй байх ба Thread-2 нь myClass.swap()-г дуудах гэж оролдох үед мутекс чөлөөлөгдөхийг хүлээж зогсох болно. Хэрэв бидэнд өөр өөр объектууд дээр 2 урсгал, 2 MyClass объект - myClass1 ба myClass2 байгаа бол бидний урсгалууд синхрончлогдсон аргуудыг нэгэн зэрэг хялбархан гүйцэтгэж чадна. Эхний хэлхээг гүйцэтгэдэг: myClass1. солих(); Хоёр дахь нь: myClass2. солих(); Энэ тохиолдолд синхрончлол нь тодорхой объект дээр хийгддэг тул swap() аргын доторх синхрончлогдсон түлхүүр үг нь програмын үйл ажиллагаанд нөлөөлөхгүй. Сүүлчийн тохиолдолд бид 2 объекттой.Тиймээс утаснууд нь бие биедээ асуудал үүсгэдэггүй. Эцэст нь хоёр объект нь 2 өөр мутекстэй бөгөөд тэдгээрийн олж авах нь бие биенээсээ хамааралгүй юм.

Статик аргууд дахь синхрончлолын онцлог

Хэрэв та синхрончлох шаардлагатай бол юу хийх хэрэгтэй вэ статик арга? анги MyClass ( private static String name1 = "Olya" ; private static String name2 = "Lena" ; public static synchronized void swap () ( String s = name1; name1 = name2; name2 = s; ) ) Юу болох нь тодорхойгүй байна. Энэ тохиолдолд мутексийн үүргийг гүйцэтгэнэ. Эцсийн эцэст бид объект болгонд мутекс байдаг гэж аль хэдийн шийдсэн. Гэхдээ асуудал бол MyClass.swap() статик аргыг дуудахын тулд бидэнд объект хэрэггүй: арга нь статик! Тэгэхээр, дараа нь юу вэ? :/ Уг нь энэ асуудалд байхгүй. Java-г бүтээгчид бүх зүйлийг хариуцаж байсан :) Хэрэв чухал "олон урсгалт" логикийг агуулсан арга нь статик байвал синхрончлолыг ангиараа хийх болно. Илүү тодорхой болгохын тулд дээрх кодыг дараах байдлаар дахин бичиж болно: анги MyClass ( private static String name1 = "Olya" ; private static String name2 = "Lena" ; public static void swap () ( синхрончлогдсон (MyClass. class ) ( String s = name1 ; name1 = name2; name2 = s; ) ) ) Зарчмын хувьд та үүнийг өөрөө бодож болох байсан: объект байхгүй тул синхрончлолын механизм нь ямар нэгэн байдлаар ангиудад "хатуу холбогдсон" байх ёстой. Энэ нь ийм байна: та бас ангиудыг синхрончлох боломжтой.