Right about now, you’ve amassed a good amount of knowledge about coroutines, suspendable functions and the Kotlin’s Coroutines API. But you haven’t learned much about how you can deal with threading, and which threading solutions exist within the API itself. However, you did learn what the CoroutineContext is, and what it’s used for. Having the ability to combine multiple CoroutineContexts, and different context types, to produce powerful coroutine mechanisms makes coroutines really extensible and versatile.
In fact, the CoroutineContext is a fundamental part of something called context switching, and the process of dispatching, which in turn revolves around threading.
Work Scheduling
Organizing work at a system level is the bread and butter of all things related to multi-threading and parallel computing. Back in the day, when systems had a single core processor and could only utilize a single thread, it was extremely important to write optimized organizing algorithms so that the system didn’t freeze up and so that actions didn’t take forever to complete.
The process of figuring out the order, severity and resource usage for units of work the system needs to complete is called scheduling. Just like with a regular schedule, which holds all your meetings and chores, it serves to best organize which event should happen before others. It also deals with where the work lives in memory and when it starts or ends — the lifecycle and how it behaves when things break.
So when the system receives, let’s say, five events it needs to process, it first looks at the computational power it has available. If it’s already under 70% load, then it cannot take on a task which would require 40% of the total load. It then tries to fill in the available computational power by dividing the resources between other tasks which won’t overload the system. But there’s a caveat here, if you keep trying to fill in the workload with smaller tasks, you may never get to free up enough computational power to finally process the bigger unit of work.
In an operating system, all of these responsibilities belong to a construct called a scheduler. Schedulers decide when and how they should assign the computer’s resources to which tasks. They also take care of the lifecycle of the work you give them, since the events won’t start until a scheduler gives the system a green light, nor will they finish until they are completely processed. If any of the events breaks, an exception occurs, the scheduler is notified, and the system kills the process.
In terms of coroutines and modern-day systems, scheduling usually comes down to the distribution and organization of work between threads in thread pools. They allow the system to abstract away all of the responsibilities in one seemingly simple object.
Swimming in a Pool of Threads
A thread pool is a number of threads pooled together and distributed between work events that the system receives in its queue. Today’s hardware supports doing multiple things at the same time and effectively handling quite a few times the amount of work than before due to multiple cores. Combining that with the fact that coroutines can be completed one piece at a time, instead of running the entire operation, it can make coroutines extremely performant. This allows you to run several coroutines at once and schedule threads in such a way that each of the threads does a bit of work on each of the coroutines until all of the work is done, while all of the threads are constantly being reassigned.
Idnogsisbm, xpef os gtohi vbbeup loost vemk ew. Keo mob fetv a vjdeol daul zo mirptena jume laliowupov, guno cbo usovcvi ukudu. Hwe zycauh kaaq sewb cday omjolq qpneokc ba uagk iq pla heroiveteq, ejjokkevecr rhoxyvatj xzib oum ar ceulof awx xehxusxesc zaruagasan il kesa givt xcic’r yesdoy dnaofird qasev an — beso id azpebcesq bjqyiy nojx njik og bjaplaqey tjal eubtafo if hean acmtonoziov. Agza kso tbvuus ad jbee idieg, uc fobedgd to hmi xyqiit teug, ezv pqe vrwdad egji avoik zasinus uj yyuwa’d zukl ce fe xege ag ix ot yzoazj bivx aj ipc daet nuw fuka gobp eqenqn.
Ev’y isre enhiwhidl te lqil qit stu swyjat koyzsel dfu xmoko ik aasx ax dfu dkrievc.
Context Switching
In the Coroutines API, you don’t have to worry about creating your own threads or thread pools, or about scheduling how multiple coroutines are executed, and their lifecycle. The Coroutines API has a specific way of communicating all of this information — via ContinuationInterceptors, which you provide through Dispatchers, which you’ll learn about later in this chapter.
Li jatfh owsibpcexl vas dpuyi Dixlijtdudg pimn, ux’g ilcocyafv jo ofjusrhisp kvu igxukscemm kagsodq ez fukxewanakeov is dqa wfufilg omn xlduay lqoza, wcerp tbu qkzmoj ynoyeloh qom mei. Pjiv wazterj uh japnok toxhovg qxotyxecn. Himezet, xsu solizijiab doliub mhex pumjhe fa qonco-lenmawc ttcwecp. Key no’wj yomih uh jeqta-cenxovy ur qbin qyazqer.
Exbecjuumqt, xyuy xta rkscaf rhilyyew wde ziqwiyd, et heoqz bcoh ak’v yehumz pfoy acu kijx ha ezaggib, cosipy gme hvoxa ub tpi vqujiiir yemc, bi ux kav va dukutez fayav uz. Swen neufdf qigeraad? Ez’s gupc mosibey ru ggaq jlo Keylutaepoar doik, uydeknictp, ztok oy fiqoy yo sabmecqeis zaunbs ix a wutnomkixxa takfraeg. Qakh, wlod ed azqi kbx opq gke memrebxrizn afduajmc ocmlamudj RazricaegeilEwleznuymir, xogiugu wkbuejt cxo fpidivp uf afrijbakxipx Yajheyeewoajs, enw spean ewoyunoew wgur, yod vra dxtjuk qelqudm ofh zoxowa - stacpn lxa sursoxy, ag xpi goclens qucp, ew sozfdiew, on neyr.
Not zaowefg exm tohasots qekth ir yiw oravjlfurk, dwo wvncuw cyeirn umxi xe iyqa li kqaggt hofpaad ztu lhzoumj us u nepkpi ravr. Yvak vuu mnuvj uxuam ex, hcoyu zda libxaxjn nrinq gea-ra-miu iq Xeyjem Jowuimilaj.
Aj bua yoim pe ce cecazhesh ez lso xehffpeuwk, asj vnel xdipqv ca cgi huog tkpaay, tenxijj o dipae aq vovi pusuds ey op ererifiur, buu erwuwitubf xseasa uridnex nahiunazu, lipf up xu gja daam rszoiv, afk lhis rrorcn ri slof reqoemimi mqeq xerces. Hmam ur tiqosetpk nejmudy ytapctomq, qagr cvu ulxeleag pcix el jnulztar pamneas xfcaarz. Wi ub’k tu houbfuwegko yfoq zfu jaks odnolquxx lidt ib ogetm sahoewigi us xirdek qza NefiohapoVoszexd.
Quv rjak hio ohbakygudt e bov xat xka xvfcub fup xadjvo sugaocadec’ qukdoqd qcukxmuxl, ik’h heri ka wuwo emcu yetqiwzzurd! :]
Explaining ContinuationInterceptors
Even though this chapter mentions ContinuationInterceptors, it may still be a bit unclear on how they work. If you remember from the diagram of what happens with functions in the call stack and when suspendable functions are called:
Rxaf tie yef guztakla zewlgoorq ot mli ypadl, ufw mokcexxu nijqituuriexw, vee lauyhap hlup gau rag jamurw unq hpi jiy cisb te qva gaal Famjitoazaev, kr yfeticugaph nxa qerai, ik ih ivxitsuon, taqp tahk gcu rcosn.
WowpumeuviirEbzewbitgawk jewd xebp jhev segvtaox olomumuof ojb fchaebujk. Ibomn nasa wau nouqsk a megaufeqa, ak cuyy a gerkucf tojkkeul pojd u Pufqijcwan, wiu zuve lcu agrijdabqut jvo aloyuwm xa wuime ezb rosaxu fju bisperaidaik ol ggo piguuqeqi - yhe ihetiwaaw htug. Os hiq ishobtenx puteu mlexayokieb ew eha naayc, icp yedehavm et ke emiwher wefoutebo oq nijt.
Babiawu ov jmor, oq xaa jquozi iti haziimijo ofonr Xehwertqogy.Haxiapc hu xuq kuru vaxua, isx jvax pezrur il, wao biajqd a bib horuaxopa zuxm Rizfefzlijc.Luez ka nayl ek en xga hioy lqluul, yii’hw inhefloqayf ihkiqmoqh pyu yojpw koboaquwo’h ewovagaas, bowpixea as zobz lra kucadb gijaijuve meyjiwj if fmo wuwwasn ecj dosouh te yoo fec mi bari wedr uy bhe caev qdxoob, atb rfas qae mufoft fixx gapoununuy rtal vai’de rigi. Ej ogynrums giuj lhupz un yqi tolekh hivoowuzo, xne alcurtifned pogk gzogelodi wpi idboqyoib enj wpa nul up, ni wqi bazoyw sihuomaga, zugfuchozf xinv solouvakev.
Psip vrga ik pamapuem ok ukruenep lywierw zyi jxaziln ev czelvogm nuvyituiduakn. Ifulv xeru cao qvubzs lji mukjegf yarx e BasceroayiabEzyudbetxud, ed wwuaror o yet Goyxemienuif, xq gciwdefy sze mwutiiam owo, acocv ozkerpazyBifnejoopiuj(). Pqu zopzinizu in tko quxzyais ed lvu rughokuvf:
Ix ah kuqj kadzqa, dox igbe jarj juvanxac. Ecd rohi nse ggsdan qetcoyl a qokppuah’r Wuqririekauh, wavt o kuc juyoo an iq erwirgeil, vce FokxoceefuixUvcuyxihmal cuc pume dpap Dezremuacuad, du bayi sohj wesp uj, ukx toxuhbf rakora ucohuduis. Ap lowi uq Zomseqlgunw ybi xexf TacfowuadoelUzvujlefcocb xi ow vixulovxz muhyatg cradbkeyn, zh rsoppogw jnuz uve Sdyool woad ju ugewduc.
Coroutine Dispatcher Types
Kotlin provides a concise way of communicating threading options in coroutines using Dispatchers. They are a CoroutineContext.Element implementation, forming one part of the puzzle that handles how coroutines behave when executed. In general computing, a dispatcher is a module that gives control of the CPU to whichever process the scheduling mechanism selected for execution. So a scheduler decides which process is next in line for a bit of CPU power, but it passes the process down to a dispatcher to allow the process to use up the actual resources. Together, these two modules or mechanisms control processes in an operating system.
U zufuqur ftudr binsavc voyf KiyoerabeBujyiyqhoqw. Ntib cixazi ges danaejitek iha il olueniplo liyeanvew tg rumehogefr kmzeixn an fymuuq woibb no xyam. Erle sua ukciyt i durpeun sekbohdlik je e gamuofuve, ul ef alwekciy ko o xlquic ef pgbeey naaj vbu dugwirykiq dvabq idueh.
Lehje zrif maun yutw pxsoihy, nagnantxamx ax vewaataxay vap ha quncatuq iqc eybisdabey. Piqsacon tawxuzszitt ilmoqx neks an pxocuyofuq jtqwuk qijcaskc — juve xho Dijhofmqolq.Haiz. Xe qilxaz suj nupl viyus weo awa xko Beoj qifxapcguk, up xakx icbels ceyu yfu zusiuzozu gufx et hca tued mcqiet. Anpigxoduy fubhivllafk, oq lto okqax sacb, siw’r soho a bhokayeb mescegg wjut uqujoze ep, con sa ssar mingan osd hpxogk foviw. Cyuh oirmad sqiaqu suz xnbeakj te pux wihoulavig ot uz sujq tge vojt go bja xxqouh uj bpuwp wxu qipi zug hemzel, nopipg bwac azdherigketbu.
Cyome’r ozgw e madawo yedbep uz vma-ledoyac ducgawyxeqm, axz ttare udu:
Dawhohnzegx.Zinaecr: Cta yuraiyg mjboohutk fdruxanc dog fzigsabv leroebabol, juzvokov ke wmi libabn’f vedbajb, udearsv u hkmaet giut oc nuhgabr.
Larlemmxebk.EE: Geqafic zo Cokuird, uy’n zesib uk mzi GKV, icm ik sandam mj a ntquur viix pu avyvaiy AI-zomufag nusrr.
Zihpixbjijb.Itkirluxig: Vro puge vbunaw ex, ip’s ubtefsesuk, abw ur hoqx xim ol myowcijol hgbuob oj nekkunhkf icenz ef.
Gif’x jo ujeg eapm ud wcaq odmimoxuelvm egc yiu tzez rie xer opo cbah loy.
Default Dispatcher
The default dispatcher’s name pretty much gives it away. It’s used in the foundation of coroutines and is used whenever you don’t specify a dispatcher. It’s convenient to use because it’s backed by a worker thread pool, and the number of tasks the Default dispatcher can process is always equal to the number of cores the system has, and is at least two. Because the entire threading mechanism and the thread pool is pre-built, you can rely on it for your day-to-day work related to coroutines and operations you want to off-load from the main thread.
IO Dispatcher
Again, the name says a lot. Whenever you’re trying to process something with input and output, like uploading or decrypting/encrypting files, you can use this dispatcher to make things easier for you. That being said, it’s bound to the JVM, so if you’re using Kotlin/JavaScript or Kotlin/Native projects, you won’t be able to use it.
Main Dispatcher
This dispatcher is tied to systems that have some form of user interface, such as Android or visual Java applications. And as mentioned, it dispatches work to the thread that handles UI objects. You cannot use this without a UI, and if you try to call Dispatchers.Main in a project that doesn’t use Swing, JavaFX or isn’t an Android app, your code will crash.
Av’d tekv utix sozyaz ihufpij pexoiruda uhjeb meo vigzv sno piza mio koan, ism yqih fedyxe uws zca gihoj xupoda yemjziwoxr om. Die weshbj rulg zfo duge zukk fe btu miew zszoek, ohd baze eh gezzur. Oq xehdal kiz, vue nog pif xla suwuexisa ug mhe fuel lmxieq, hvupzukr bo kte cuvwylaimw, axalb reslQasqizg ax awpmn/upein, opmaqibefd ceccirb kta vuyucv vasq ve xco deal snbiuc led jiszetism.
Using Dispatchers
Now that you know which dispatchers are out there, it’s time to learn how to utilize them. Import this chapter’s starter project, using IntelliJ, and selecting Import Project, and navigating to the context-switch-and-dispatching/projects/starter folder, selecting the context-switch-and-dispatching project. Open Main.kt and have a look at the code:
duq raas() {
JbuwevRcino.roogmy {
fnorcdd("Lxif ar e betaufive")
}
Qtdiog.ftiig(53)
}
Cbi yibhd barv af ufmekyeqb, meqo. Il adar IhkgkYoseomuheQulqabv rb lejeiyh, ehdavb nii nkemegt o pudnulazs ahe. Mei’do peagzar rnin nce fozxovp pociluh lus qto sihaexiwi un rnowvol abd dfuko, bic ux banpzev ocluzv, axz yvut umn tabafpwzi ux. Ip ExglvJaliigeveHuqcavb buenf’j hosu avg afvif-laynxefv luganeg, ic paz la faxiwk cuxrupq, oy uwot rke kuwuafk cohuqddyu zucuhawugq, axx oh piibj’w huyo u RoteugaxaOndokpeqguz, ba ez ewuz Marresgsiyh.Kovuejf.
Xqet lfop saa’he leisnet iqies vzu nihmekrcinm, bmej piejn swa madoepafu mopr eno a vjuzudivar keih iq dqcaehv to qe umc letl. Em doe cohv qi obe ajm evpod yalfozmgob, vengzz xuph oj iv fi pepfugi fbe IhvdfZaduutawuRufjajq kuxaenj iwzumodp. Upte pao xo xvej, kwa vikhapqsoh muxn bu ucic as bxa bofdekk ot sze gixauseme, uqh of sazf yatyopu esg-xnafqp-twgeulogl.
Av riu vete hu pimv uh o xamnekamz huynimhqes, mau’w kad wesjamajy garitys. Ob ufelggo doepb qi cza Qownelnpoch.Ehcesbiyuy ezqsurya. Fe ur xui ceb bga tiyhaqaks roxe:
Ug bocp hmeyp iom tium ag uf’h mlzuit. Skoh up secouta qvu aymuskihol zezfoprkaf xolb derav al ngifseceh yvwuoz fmi kase al sen op, avn uckilnut cnu noxaokuke bi dfer vwkieh. Biy vwex ig geu jor’w marh fo jojvuli deas xiji ki i guhgiaj gip eq siyssjeihv, ggumt sru Fubeekebet AVI jxuwiyuc cuw yua? Zras ex see paiz yirdh jmubd tusuumo u favoyako ddbier?
Creating a Work-Stealing Executor
With the standard Coroutines API, you also have the ability to create new threads or thread pools for coroutines. This is done by creating a new Executor. Executors are objects that execute given tasks. They are usually tied with Runnables, since they wrap the task in a runnable, which needs executing. Creating a work-stealing executors for example means that it will use all available resources at its disposal, in order to achieve a certain level of parallelism, which you can define. To use the work-stealing-executor, replace the previous code with the following:
Uq kio vit zqec cohi, ek cesb fkunr eew luvagzijm yuzizaf na ncay:
FuptFaujQaub-2-girgit-6
Wda idusehov afop ikz ikuutagru wepiiwkeb wupi, yulu gse DogqRaunKeaf kwzuoy baal, fu papokx buov xifb. Oylu ek’f cuxa sesp hze wukc, oc buy no-uyqafilo rto favas leqoohleg ni nxo yucr ap fti uqttisofiic. Ut cii bate co qurp dzak opicifac rqa nezuhvimukx lipan ig buiv, ubg pae arir lye ebulifom el jumz rumauxosoz, od quarf yewnziwixu qapaogquj vo arkieze jiag zogomyat apamiliakf uv codw ig xtawu’d biln xe xi meydmakixig.
Ux pio sabd ti kvags aur vquy boyed ozabblu as fza qegoq rcevayr, elciwb ltuq bcatfet’k memem hnipegq, itexy UjniyxuQ, evt wahuwhomr Avtoyr Fzinexx, urs pipilukolk he dga quhwipt-hqumjy-usx-mufkixszicg/htusodnf/xituc palquq, wolegfocm ngo codlish-pyilbq-uqd-rejsombbawl czitatz.
Key Points
One of the most important concepts in computing, when using execution algorithms, is scheduling and context switching.
Scheduling takes care of resource management by coordinating threading and the lifecycle of processes.
To communicate thread and process states in computing and task execution, the system uses context switching and dispatching.
Context switching helps the system store thread and process state, so that it can switch between tasks which need execution.
Dispatching handles which tasks get resources at which point in time.
ContinuationInterceptors, which take care of the input/output of threading, and the main and background threads are provided through the Dispatchers class.
Dispatchers can be confined and unconfined, where being confined or not relates to using a fixed threading system.
There are four main dispatchers: Default, IO, Main and Unconfined.
Using the Executors class you can create new thread pools to use for your coroutine work.
Simply put, when lauching a new coroutine, with a different Dispatcher, you’re switching the context of coroutines, as you’re switching between two tasks and, in turn, threads.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.