فر

فرایند در Elixir

5 تمرین

درباره‌ی فرایند

در Elixir، همه‌ی کدها داخل فرایندها اجرا می‌شوند. فرایندهای Elixir:

  • نباید با فرایندهای سیستمی اشتباه گرفته شوند.
  • سبک هستند.
  • کاربردهای مشخصی دارند. می‌توانند:
  • طبیعی است برنامه‌ای Elixir داشته باشید که صدها فرایند اجرا کند، و همچنین طبیعی است برنامه‌ای داشته باشید که اصلاً فرایند جدیدی به‌طور صریح ایجاد نکند، به‌ویژه اگر کتابخانه باشد.

ساخت فرایندها

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

  • spawn/1 تابعی را می‌پذیرد که آن را مستقیماً اجرا می‌کند.

    spawn(fn -> 2 + 2 end)
    # => #PID<0.125.0>
    
  • spawn/3 تابعی را می‌پذیرد که آن را با اسم ماژول، اسم تابع (به‌صورت «اتم») و فهرستی از آرگومان‌هایی که به آن تابع داده می‌شوند اجرا می‌کند.

    spawn(String, :split, ["hello there", " "])
    # => #PID<0.113.0>
    
    • به این سه‌گانه‌ی داده اغلب MFA می‌گویند: ماژول، تابع، آرگومان‌ها.
  • یک فرایند به‌محض اینکه اجرای تابعش تمام شود، پایان می‌یابد.

  • می‌توانید با Process.alive?/1 بررسی کنید که آیا فرایندی هنوز زنده (در حال اجرا) است:

    pid = spawn(fn -> 2 + 2 end)
    Process.alive?(pid)
    # => false
    

پیام‌ها

فرایندها اطلاعات را مستقیماً با یکدیگر به اشتراک نمی‌گذارند. فرایندها برای به اشتراک گذاشتن داده، پیام می‌فرستند. به این الگوی همروندی، «مدل Actor» می‌گویند.

  • با send/2 به یک فرایند پیام بفرستید.

    send(pid, :hello)
    
    • پیام به همان ترتیبی که فرستاده شده، در صندوق پیام گیرنده قرار می‌گیرد.
    • send بررسی نمی‌کند که پیام دریافت شده است یا نه، و نه اینکه گیرنده هنوز زنده است یا نه.
  • پیام می‌تواند از هر نوعی باشد.

  • می‌توانید پیامی را که به فرایند جاری فرستاده شده، با receive/1 دریافت کنید.

    • باید پیام‌ها را تطبیق الگو کنید.
    • receive صبر می‌کند تا پیامی که با یکی از الگوهای داده‌شده تطبیق داشته باشد در صندوق پیام فرایند قرار بگیرد.
      • به‌طور پیش‌فرض، بی‌نهایت صبر می‌کند، اما می‌توان با یک بلوک after برایش مهلت زمانی تعیین کرد.
    • پیام‌های خوانده‌شده از صندوق پیام فرایند حذف می‌شوند. پیام‌های خوانده‌نشده تا ابد همان‌جا می‌مانند.
      • همیشه در receive/1 یک بند جامع _ بنویسید تا به دلیل انباشته شدن پیام‌های خوانده‌نشده، حافظه تمام نشود.
    receive do
      {:ping, sender_pid} -> send(sender_pid, :pong)
      _ -> nil
    after
      5000 ->
        {:error, "No message in 5 seconds"}
    end
    

حلقه‌ی دریافت

اگر می‌خواهید بیش از یک پیام دریافت کنید، باید receive/1 را به‌صورت بازگشتی فراخوانی کنید. الگوی رایجی است که یک تابع بازگشتی پیاده‌سازی کنید، مثلاً با اسم loop، که receive/1 را فراخوانی می‌کند، کاری با پیام انجام می‌دهد و سپس خودش را فراخوانی می‌کند تا منتظر پیام‌های بیشتری بماند. اگر لازم است حالتی را از یک فراخوانی receive/1 به فراخوانی دیگر منتقل کنید، می‌توانید این کار را با دادن یک آرگومان به آن تابع loop انجام دهید.

def loop(state) do
  receive do
    :increment_by_one ->
      loop(state + 1)

    {:report_state, sender_pid} ->
      send(sender_pid, state)
      loop(state)

    :stop ->
      nil

    _ ->
      loop(state)
  end
end

در عمل، این روش به‌ندرت مستقیماً استفاده می‌شود. Elixir انتزاع‌های همروندی ارائه می‌دهد، مانند ماژول Agent یا رفتار GenServer، که هر دو روی حلقه‌ی دریافت ساخته شده‌اند. با این حال، درک این مبانی برای استفاده‌ی کارآمد از این انتزاع‌ها ضروری است.

ویرایش از طریق GitHub این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود

فرایند را یاد بگیرید

تمرین کردن قفل شده است

برای تمرین فرایند قفل 4 تمرین دیگر را باز کنید