به همراه ارائه آموزش کاتلین، دانشجویار اقدام بررسی آموزش Coroutines در کاتلین کرده است. با ما همراه باشید.

مقدمه آموزش Coroutines در کاتلین

برای آموزش Coroutines در کاتلین، در ابتدا نیاز است که بدانیم یک Coroutine چیست و آن‌را درک کنیم. کاتلین به‌عنوان یک زبان برنامه نویسی، فقط تعداد محدودی از API های سطح پایین را در کتابخانه استاندارد خود فراهم کرده است تا Coroutine ها مورد استفاده قرار بگیرند. برای درک بهتر، مثلا Async و Await بر خلاف دیگر زبان‌های برنامه نویسی با قابلیت‌های مشابه، کلیدواژه به حساب نمی‌آیند و حتی در کتابخانه استاندارد کاتلین هم قرار نمی‌گیرند. علاوه‌براین، کانسپت suspending function در کاتلین، یک برداشت امن‌تر و کمتر مستعد ارور از عملیات آسنکرون می‌دهد.

اما پیش از آنکه آموزش Coroutines در کاتلین را آغاز کنیم، باید با مفهوم آن آشنا شویم.

Coroutines در کاتلین چیست؟

تیم کاتلین، کوروتین ها را “تردهای سبک وزن” توصیف می‌کند. در واقع آنها، نوعی از تسک‌هایی هستند که توسط تردهای واقعی اجرا می‌شوند.

جالب اینجاست که یک کوروتین در حال اجرا می‌تواند توسط یک ترد در suspension point های خاصی متوقف شود و به کار دیگری بپردازد و حتی توسط یک ترد دیگر به کار گرفته شود.

برای درک بهتر، یک Coroutine دقیقا یک تسک نیست. بلکه قسمتی از تسک‌های فرعی است که باید در یک ترتیب خاص و تضمین شده، اجرا شوند.

نکته
پشنهاد مقاله مکمل: معرفی کتاب آموزش کاتلین(Kotlin)

Co + Routins = Coroutines

Co مخفف واژه Cooperation به معنای همکاری و Routins به معنای توابع است. به این معنا که وقتی توابع (Routins) با یکدیگر همکاری (Cooperation) می‌کنند، این عملیات را Coroutine می‌گوییم.

به تعریف دیگر، کوروتین نمونه‌ای از یک محاسبه تعلیق پذیر بوده و از نظر مفهومی شبیه به یک ترد است؛ یعنی یک بلوک از کدها اجرا می‌شود که همزمان با بقیه کدها کار می‌کند. با این حال، کوروتین به ترد خاصی محدود نمی‌شود. اجرای آن ممکن است در یک ترد متوقف کرده و در یک ترد دیگر از سر گرفته شود.

حال به آموزش Coroutines در کاتلین خواهیم پرداخت.

نکته
پشنهاد مقاله مکمل: خروجی ios از کاتلین

اولین coroutine

کد زیر را برای ساخت اولین coroutine خود اجرا کنید:

fun main() = runBlocking { // this: CoroutineScope
    launch { // launch a new coroutine and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello") // main coroutine continues while a previous one is delayed
}

اجرای این کد، نتیجه زیر را در پی خواهد داشت:

Hello
World!

بیایید عملکرد این کد را کالبدشکافی کنیم.

lunch یک coroutine ساز است که همزمان با بقیه کد، یک کوروتین جدید راه اندازی می‌کند تا به طور مستقل به کار خود ادامه دهد. به همین دلیل است که ما Hello را در ابتدای نتیجه، مشاهده می‌کنیم.

delay یک suspending function ویژه است و کوروتین را برای مدت مشخصی به حالت تعلیق در می‌آورد. تعلیق coroutine ترد زمینه را مسدود نمی‌کند، اما به سایر coroutine ها اجازه می‌دهد تا ترد زمینه را برای کد خود اجرا کرده و استفاده کنند.

اجرای روتین‌ها

هر بار که فرایندی را شروع می کنید -برای مثال راه اندازی یک برنامه- رایانه شما چیزی به نام روتین اصلی ایجاد می‌کند. این بخش اصلی هر برنامه است؛ زیرا در اینجاست که شما سایر اجزای کد خود را تنظیم و اجرا می‌کنید. مانند اصلی‌ترین نمونه‌های یادگیری، اغلب یک عملکرد اصلی دارید که Hello World را ایجاد می‌کند. این عملکرد اصلی، نقطه ورود برنامه شما بوده و بخشی از روتین اصلی است.

اما با بزرگتر شدن برنامه‌های شما، تعداد توابع و تعداد تماس با سایر توابع نیز افزایش می‌یابد. هر زمان که یک تابع دیگر را در بلوک اصلی فراخوانی می‌کنید، چیزی به نام روتین فرعی را شروع می‌کنید. یک روتین فرعی، فقط یک روتین معمولی است که در یک روتین دیگر قرار دارد. رایانه، همه این روتین‌ها را روی پشته تماس قرار می‌دهد. ساختاری که پیگیری می‌کند چه چیزی در حال اجرا است و چگونه روتین فعلی نامیده شده است. پس از اتمام اجرای یک روتین عادی، از پشته خارج می‌شود و کنترل دوباره به روتین تماس گیرنده منتقل می‌شود. در نهایت، اگر پشته خالی است و هیچ چیز دیگری برای اجرا وجود ندارد، برنامه به پایان می‌رسد.

استناد به یک روتین فرعی، مانند انجام یک تماس مسدود کننده است. یک روتین یک روال فرعی است که می‌توانید به عنوان یک تماس بدون انسداد فراخوانی کنید. به همین دلیل، تفاوت اصلی بین یک روتین استاندارد و یک روتین، در این است که دومی می‌تواند موازی با سایر کدها اجرا شود. می‌توانید آنها را شروع کرده و فراموش کنید و به بقیه برنامه بروید.

لانچ یک Coroutine

برای پیروی از کد این فصل، پروژه شروع این فصل را با استفاده از IntelliJ وارد کنید و Import Project را انتخاب کنید. به پوشه start-start-with-coroutines/projects/starter رفته و پروژه get_started_with_coroutines را انتخاب کنید.

وقتی پروژه باز شد، Main.kt را پیدا کرده و باز کنید. در آنجا کد زیر را پیدا خواهید کرد:

fun main() {
  (1..10000).forEach {
    GlobalScope.launch {
      val threadName = Thread.currentThread().name 
      println("$it printed on thread ${threadName}")
    }
  }
  Thread.sleep(1000)
}

از آنجا که راه اندازی اولین کوروتین شما چندان جذاب نیست، ده هزار کوروتین اول خود را راه اندازی می‌کنید! در حال حاضر، راه اندازی ده هزار موضوع برای رایانه کمی خسته کننده است و اکثر برنامه‌ها دارای OutOfMemoryException هستند. اما از آنجایی که کوروتین ها بسیار سبک هستند، می‌توانید تعداد زیادی از آنها را بدون هیچ تاثیری در عملکرد راه اندازی کنید. اگر برنامه را اجرا می‌کنید، باید متن زیادی را مشاهده کنید. هر خط می‌گوید که کدام شماره را روی کدام ترد پرینت می‌کند.

در آموزش Coroutines در کاتلین، چند نکته مهم وجود دارد که باید به آنها توجه کنید. اولین مورد در مورد بدنه coroutine است که توسط بلوک کد ارائه شده، به عنوان پارامتر برای راه اندازی () نشان داده می‌شود که به آن سازنده coroutine گفته می‌شود.

دوم، هنگام راه اندازی کوروتین‌ها، باید CoroutineScope را ارائه دهید. زیرا آنها مکانیسم‌های پیش زمینه ای هستند که واقعاً به چرخه زندگی نقطه شروع آنها اهمیت نمی‌دهند. اگر برنامه قبل از تکمیل بدنه به پایان برسد چه اتفاقی می‌افتد؟ در این مورد، شما از چیزی به نام GlobalScope استفاده می‌کنید که این واقعیت را تصریح می‌کند که چرخه حیات coroutine به چرخه عمر برنامه متصل است. به همین دلیل، شما همچنین باید موضوع فعلی را در حالت تعلیق قرار دهید و Thread.sleep (1000) را در انتهای main () صدا کنید.

این توضیحات، اساس اصلی کاری است که باید انجام دهید. اما این مفاهیم پیچیده‌تر از آن هستند که بتوان در یک مقاله کاملا آموزششان داد.