0Pricing
Elixir & Phoenix: Scalable Backend Development · درس

عمليات Elixir وتمرير الرسائل

تعلّموا عن عمليات Elixir خفيفة الوزن، وكيفية تواصلها، ومبادئ التزامن القائم على «عدم مشاركة الحالة».

عمليات Elixir وتمرير الرسائل درس مجاني في Elixir & Phoenix: Scalable Backend Development على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Elixir & Phoenix: Scalable Backend Development، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Elixir & Phoenix: Scalable Backend Development 4 دروس في المجموع.

بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.

Meet Elixir Processes

Elixir is built for concurrency, and processes are its core building blocks. Think of them as tiny, isolated programs running simultaneously.

Unlike operating system processes or threads, Elixir processes are incredibly lightweight. You can have hundreds of thousands, even millions, running on a single machine!

  • Isolation: Each process has its own memory.
  • Communication: They talk to each other by sending messages.
  • Fault Tolerance: If one crashes, it doesn't bring down others.

Creating New Processes

We create a new Elixir process using the spawn function. It takes a function (often an anonymous function) that the new process will execute.

spawn returns a Process Identifier (PID), which is like an address for the new process.

defmodule MyModule do
  def greet do
    IO.puts "Hello from a new process!"
  end

  def run do
    # Spawn a new process that calls MyModule.greet()
    pid = spawn(MyModule, :greet, [])
    IO.puts "Spawned process with PID: #{inspect(pid)}"
  end
end

MyModule.run()

Understanding Process IDs (PIDs)

A Process Identifier, or PID, is a unique reference to an Elixir process. It's how you address a specific process to send it messages.

Think of a PID like a phone number for a person. You dial their number to talk to them, and processes use PIDs to communicate.

  • Every process has a unique PID.
  • PIDs are essential for message passing.
  • You can inspect a PID to see its internal representation.

Finding Your Own PID

Just like you can get the PID of a newly spawned process, any running process can find its own PID using the self() function.

This is crucial when a process needs to tell others how to send messages back to it.

defmodule PidDemo do
  def show_self_pid do
    IO.puts "My PID is: #{inspect(self())}"
  end

  def run do
    IO.puts "Main process PID: #{inspect(self())}"
    # Spawn a process to show its own PID
    spawn(PidDemo, :show_self_pid, [])
    :timer.sleep(100) # Give the spawned process time to run
  end
end

PidDemo.run()

Communicating with Messages

Elixir processes communicate by sending and receiving messages. This is the core of the 'share nothing' concurrency model.

Messages are simple Elixir terms (any data type) sent from one process to another's "mailbox."

  • send(pid, message): Puts message into the mailbox of the process identified by pid.
  • receive do ... end: Waits for messages in the current process's mailbox.

Answering Back

Let's see how a "client" process can send a message to a "server" process and receive a reply.

The server process uses a receive block to wait for and handle incoming messages. It then sends a reply back to the client's PID.

defmodule EchoServer do
  def loop do
    receive do
      {:echo, client_pid, message} ->
        send(client_pid, {:reply, message})
        loop() # Continue looping to receive more messages
    end
  end
end

defmodule Client do
  def run do
    # Start the server process
    server_pid = spawn(EchoServer, :loop, [])
    IO.puts "Echo server started with PID: #{inspect(server_pid)}"

    # Send a message to the server, including our own PID for reply
    send(server_pid, {:echo, self(), "Hello, server!"})
    IO.puts "Client sent 'Hello, server!' to #{inspect(server_pid)}"

    # Wait for a reply
    receive do
      {:reply, message} ->
        IO.puts "Client received reply: '#{message}'"
    end
  end
end

Client.run()

Why Share Nothing Matters

Elixir processes embody the 'share nothing' principle. This means processes do not share memory or state directly.

Instead, they communicate exclusively through message passing. This design choice offers significant benefits:

  • Isolation: Prevents one process from corrupting another's data.
  • Concurrency: Easier to reason about and scale across multiple CPU cores.
  • Fault Tolerance: A crash in one process doesn't affect others, making systems more robust.

Smart Message Handling

The receive block is incredibly powerful because it uses Elixir's pattern matching.

You can define different clauses within receive to match specific message structures, ignoring others until a match is found.

defmodule SmartReceiver do
  def loop do
    receive do
      {:greet, name} ->
        IO.puts "Hello, #{name}!"
        loop()
      {:farewell, name} ->
        IO.puts "Goodbye, #{name}!"
        loop()
      :quit ->
        IO.puts "Receiver quitting."
      _ -> # Catch-all for unmatched messages
        IO.puts "Received an unknown message."
        loop()
    end
  end

  def run do
    receiver_pid = spawn(SmartReceiver, :loop, [])
    send(receiver_pid, {:greet, "Alice"})
    send(receiver_pid, "Just a string")
    send(receiver_pid, {:farewell, "Bob"})
    send(receiver_pid, :quit)
    :timer.sleep(100) # Give processes time to finish
  end
end

SmartReceiver.run()

Keeping State with Recursion

Since processes don't share memory, how do they maintain state? Through recursion!

A process passes its current state as an argument to itself when it calls its loop function again. This creates an immutable, sequential flow of state changes.

defmodule Counter do
  def loop(count) do
    receive do
      :increment ->
        IO.puts "Incrementing to #{count + 1}"
        loop(count + 1)
      :get_count ->
        IO.puts "Current count: #{count}"
        loop(count)
      :stop ->
        IO.puts "Counter stopped at #{count}"
    end
  end

  def run do
    counter_pid = spawn(Counter, :loop, [0])
    send(counter_pid, :increment)
    send(counter_pid, :increment)
    send(counter_pid, :get_count)
    send(counter_pid, :stop)
    :timer.sleep(100)
  end
end

Counter.run()

Processes & Messages Check

Consider the following Elixir code snippet:

defmodule Quiz do
  def server_loop do
    receive do
      {:ping, client_pid} ->
        send(client_pid, :pong)
        server_loop()
    end
  end

  def client_action(server_pid) do
    send(server_pid, {:ping, self()})
    receive do
      :pong ->
        IO.puts "Received pong!"
      _ ->
        IO.puts "Received something else."
    end
  end

  def run do
    server_pid = spawn(Quiz, :server_loop, [])
    client_action(server_pid)
  end
end

Quiz.run()

What will be printed to the console when Quiz.run() is executed?

Your First Steps in Concurrency

Congratulations! You've taken a big step into Elixir's powerful concurrency model.

Here's a quick summary of what we covered:

  • Elixir Processes: Lightweight, isolated execution units.
  • PIDs: Unique identifiers for processes, used for addressing.
  • spawn & self(): Functions to create processes and get their PIDs.
  • Message Passing: The primary way processes communicate using send and receive.
  • 'Share Nothing': Processes don't share memory, leading to robust and scalable systems.
  • Pattern Matching: Used within receive to handle different message types.
  • State: Maintained through recursive function calls.

These fundamental concepts are the bedrock for building highly concurrent and fault-tolerant applications in Elixir!

الأسئلة الشائعة

هل درس «عمليات Elixir وتمرير الرسائل» مجاني؟

نعم — نص درس «عمليات Elixir وتمرير الرسائل» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Elixir & Phoenix: Scalable Backend Development، انتقل إلى CoddyKit PRO. تتضمن دورة Elixir & Phoenix: Scalable Backend Development 4 دروس في المجموع.

ماذا ستتعلم في «عمليات Elixir وتمرير الرسائل»؟

تعلّموا عن عمليات Elixir خفيفة الوزن، وكيفية تواصلها، ومبادئ التزامن القائم على «عدم مشاركة الحالة». تتمرن على Elixir & Phoenix: Scalable Backend Development مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Elixir & Phoenix: Scalable Backend Development؟

لا تُشترط خبرة سابقة. Elixir & Phoenix: Scalable Backend Development على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «عمليات Elixir وتمرير الرسائل»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Elixir & Phoenix: Scalable Backend Development هذا؟

نعم. كل درس في Elixir & Phoenix: Scalable Backend Development يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. عمليات Elixir وتمرير الرسائل
  2. تطبيق سلوك GenServer
  3. المشرفون وبنية التطبيق
  4. العمل المتزامن باستخدام Task وAgent
← العودة إلى Elixir & Phoenix: Scalable Backend Development