Kung nagtatrabaho ka sa pagbuo ng app, alam mo na ang pagharap sa mga gawaing matagal bago tumugon ay maaaring maging isang tunay na sakit ng ulo. Ang mga coroutine ay gumaganap dito bilang isang pattern ng disenyo ng sabay-sabay Brutal para sa pagpapasimple ng asynchronous code, pinipigilan ang main thread na ma-stuck at ang user na makaranas ng kinatatakutang "application not responding" dialog.
Sa madaling salita, maaari nating isipin ang mga coroutine bilang mga thread, ngunit mas magaan at mas mahusay. Ang mahika ay nakasalalay sa katotohanan na pinapayagan ka nitong isagawa maraming sabay-sabay na gawain sa isang thread dahil sa suspensyon, na nakakatipid ng memorya at pumipigil sa pag-freeze ng user interface habang naghihintay ng tugon mula sa network o database.
Ang konsepto ng mga tungkulin ng suspensyon
Para gumana ang lahat ng ito, ginagamit ng Kotlin ang mga nasuspindeng tungkulinAng mga tungkuling ito ay may kakayahang ihinto ang pagpapatupad ng isang coroutine nang hindi hinaharangan ang thread na nagpapatakbo nito. Isipin na parang paghinto ito ng isang gawain habang hinihintay ang hiniling na impormasyon; kapag handa na ang data, ipagpapatuloy ng coroutine ang pagpapatupad nito kung saan ito tumigil.
Ang mga tungkuling ito ay maaari lamang isagawa sa loob ng ibang suspend function o sa loob ng isang coroutine. Para baguhin ang thread kung saan isinasagawa ang isang piraso ng code, ginagamit natin ang mayKontekstona nagbibigay-daan sa atin na tumalon, halimbawa, mula sa pangunahing thread patungo sa isang input/output thread nang hindi nasisira ang lohikal na pagkakasunod-sunod ng programa.
Mga Dispatcher at Mga Lugar ng Pagsasagawa
Hindi lahat ng gawain ay pare-pareho, kaya kailangan natin Mga nagpadala para sabihin sa mga tiwaling opisyal kung saan sila dapat magtrabaho. Mayroon tayong Dispatcher.Pangunahin, mainam para sa pakikipag-ugnayan sa user interface; ang Dispatcher.IOna-optimize para sa mga kahilingan sa network o pagbabasa ng file; at ang Dispatcher.Defaultna angkop para sa mga kalkulasyon na nangangailangan ng CPU, tulad ng pagproseso ng isang malaking JSON file. Para sa karagdagang impormasyon, maaari mong konsultahin ang wastong paggamit ng mga dispatcher na Main, IO at Default.
Sa kabilang banda, Saklaw Ang (mga lugar) ang kumokontrol sa siklo ng buhay. Gamitin viewModelScope o Saklaw ng siklo ng buhay Mahalaga ito upang maiwasan ang mga memory leak, dahil kung isasara ng user ang screen, awtomatikong kakanselahin ang lahat ng coroutine na naka-link sa lugar na iyon, na pumipigil sa app na subukang i-update ang isang view na wala na.
Mga Tagabuo ng Coroutine: Mula sa Paglulunsad hanggang sa Async
Para makapag-set up ng coroutine, mayroon kaming ilang builder na magagamit. Ang pinakakaraniwan ay ilunsadna ginagamit para sa mga gawaing hindi nagbabalik ng anumang halaga (fire at forget). Sa kabaligtaran, kung kailangan natin ng resulta, ginagamit natin async, na nagbabalik ng isang bagay IpinagpalibanUpang makuha ang pangwakas na halaga ng huli, ginagamit namin ang function maghintay(), na siyang nagpapatigil sa pagpapatupad hanggang sa maging available ang datos.
meron din runBlockingPero tandaan, ganap nitong hinaharangan ang kasalukuyang thread. Hindi mo ito dapat makita sa production code, dahil sinisira nito ang pilosopiya ng mga coroutine; ang tunay na kapakinabangan nito ay nasa pagsulat ng mga pagsusulit sa yunit o sa mga simpleng pangunahing function ng console.
Ligtas na Komunikasyon sa pamamagitan ng mga Channel
Kapag kailangan natin ng dalawang coroutine para makipag-ugnayan sa isa't isa, ang pagbabahagi ng mga global variable ay isang recipe para sa kapahamakan dahil sa mga kritikal na seksyon. Dito matatagpuan ang Channelalin ang mga istruktura ng datos ligtas sa thread dinisenyo para sa pagpasa ng mga mensahe sa pamamagitan ng mga function magpadala y tumanggap.
Ang isang channel ay maaaring gumana sa pamamagitan ng dinamika ng pagtatagpo (kung saan ang nagpadala at tumatanggap ay dapat magkasabay sa oras) o maaari itong maging isang BufferedChannelNagbibigay-daan ito sa pag-iimbak ng isang tiyak na bilang ng mga mensahe bago harangan ang nagpadala. Para sa karagdagang seguridad, isa lamang ang maaari naming ilantad ReceiveChannel o isang SendChannelkaya nililimitahan ang magagawa ng labas sa channel at pinoprotektahan ang integridad ng data.
Mga Advanced na Pattern at Daloy ng Data
Sa mundo ng mga channel, may mga pattern tulad ng Fan-outkung saan maraming tiwaling gawain ang kumukunsumo sa iisang channel, na naghahati sa gawain, at ang Fan-inkung saan maraming transmiter ang nagpapadala ng data sa iisang channel. Kung naghahanap tayo ng katulad ng Observer pattern, maaari nating gamitin BroadcastChannelkung saan ang bawat mensahe ay pantay na nakakarating sa lahat ng subscriber.
Hindi tulad ng mga channel, na mga "mainit" na stream (naglalabas ang mga ito ng data kahit walang nakikinig), Daloy Ito ay mga "malamig" na daloy. Nangangahulugan ito na ang datos ay nalilikha lamang kapag mayroong aktibong mamimili. Ang isang daloy ay binubuo ng paglikha ng daloy, mga intermediate operator upang salain o baguhin ang datos at, panghuli, isang terminal operator na nagpapagana sa pangongolekta ng impormasyon.
Pag-synchronize at Pamamahala ng Atomicity
Para maiwasan ang maraming thread na mag-overlap sa iisang variable, may ilang estratehiya. Maaari nating gamitin mga mutex kasama ang tungkulin nito mayLock upang madaling magarantiya ang mutual exclusion. Mayroon ding mga semaphore, na kumokontrol sa pag-access sa pamamagitan ng mga pahintulot, o ang paggamit ng mga atomic type tulad ng AtomicInteger para sa mga simpleng operasyon na hindi nangangailangan ng mga kumplikadong kandado.
Isa pang kawili-wiling pamamaraan ay ang pagkulong ng sinulidna binubuo ng pagtatalaga ng isang nakalaang thread (nilikha gamit ang bagongKonteksto ng Isang Thread) para baguhin ang isang partikular na estado. Tinitiyak nito na walang mga concurrency conflict dahil iisang thread lang ang may kontrol sa datos na iyon. Ibahagi ang impormasyong ito upang mas maraming tao ang matuto tungkol sa paksa.