ال

العمليات في 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، أي Module وFunction وArguments.
  • تنتهي العملية بمجرد أن تُكمل دالتها التنفيذ.

  • يمكنك التحقق مما إذا كانت العملية ما زالت حيّة (أي ما زالت تُنفَّذ) باستخدام Process.alive?/1:

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

الرسائل

لا تتبادل العمليات المعلومات مع بعضها مباشرةً. بل تُرسل العمليات رسائل لمشاركة البيانات. يُسمى نمط التزامن هذا نموذج الفاعل.

  • أرسل رسائل إلى عملية باستخدام 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 تمارين إضافية لممارسة العمليات