Erlang - Đồng tiền

Lập trình đồng thời trong Erlang cần phải có các nguyên tắc hoặc quy trình cơ bản sau.

Danh sách bao gồm các nguyên tắc sau:

piD = đẻ trứng (Vui vẻ)

Tạo một quy trình đồng thời mới để đánh giá Vui vẻ. Tiến trình mới chạy song song với trình gọi. Một ví dụ như sau:

Thí dụ

-module(helloworld). 
-export([start/0]). 

start() ->
   spawn(fun() -> server("Hello") end). 

server(Message) ->
   io:fwrite("~p",[Message]).

Đầu ra của chương trình trên là:

Đầu ra

“Hello”

Pid! Thông điệp

Gửi thông báo tới quy trình với số nhận dạng Pid. Gửi tin nhắn không đồng bộ. Người gửi không chờ đợi mà tiếp tục với những gì họ đã làm.‘!’ được gọi là toán tử gửi.

Một ví dụ như sau:

Thí dụ

-module(helloworld). 
-export([start/0]). 
start() -> 
   Pid = spawn(fun() -> server("Hello") end), 
   Pid ! {hello}. 

server(Message) ->
   io:fwrite("~p",[Message]).

Nhận… kết thúc

Nhận một tin nhắn đã được gửi đến một quá trình. Nó có cú pháp sau:

Cú pháp

receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard2] ->
Expressions2;
...
End

Khi một thông báo đến trong quá trình này, hệ thống sẽ cố gắng đối sánh nó với Pattern1 (có thể có Guard1); nếu điều này thành công, nó sẽ đánh giá Biểu thức1. Nếu mẫu đầu tiên không khớp, nó sẽ thử Mẫu 2, v.v. Nếu không có mẫu nào khớp, thông báo sẽ được lưu để xử lý sau và quá trình chờ thông báo tiếp theo.

Ví dụ về toàn bộ quá trình với cả 3 lệnh được hiển thị trong chương trình sau.

Thí dụ

-module(helloworld). 
-export([loop/0,start/0]). 

loop() ->
   receive 
      {rectangle, Width, Ht} -> 
         io:fwrite("Area of rectangle is ~p~n" ,[Width * Ht]), 
         loop(); 
      {circle, R} ->
      io:fwrite("Area of circle is ~p~n" , [3.14159 * R * R]), 
      loop(); 
   Other ->
      io:fwrite("Unknown"), 
      loop() 
   end. 

start() ->
   Pid = spawn(fun() -> loop() end), 
   Pid ! {rectangle, 6, 10}.

Những điều sau đây cần được lưu ý về chương trình trên:

  • Hàm lặp có vòng lặp kết thúc nhận. Vì vậy, khi một tin nhắn được gửi đi, nó sẽ được xử lý bởi vòng lặp kết thúc nhận.

  • Một tiến trình mới được tạo ra sẽ chuyển đến hàm vòng lặp.

  • Thông báo được gửi đến quá trình sinh sản thông qua Pid! lệnh tin nhắn.

Đầu ra của chương trình trên là:

Đầu ra

Area of the Rectangle is 60

Số lượng quy trình tối đa

Trong đồng thời, điều quan trọng là phải xác định số lượng quá trình tối đa được phép trên một hệ thống. Sau đó, bạn sẽ có thể hiểu có bao nhiêu tiến trình có thể thực thi đồng thời trên một hệ thống.

Hãy xem một ví dụ về cách chúng ta có thể xác định số lượng quá trình tối đa có thể thực thi trên một hệ thống là gì.

-module(helloworld). 
-export([max/1,start/0]). 

max(N) -> 
   Max = erlang:system_info(process_limit), 
   io:format("Maximum allowed processes:~p~n" ,[Max]), 
   
   statistics(runtime), 
   statistics(wall_clock), 
   
   L = for(1, N, fun() -> spawn(fun() -> wait() end) end), 
   {_, Time1} = statistics(runtime), 
   {_, Time2} = statistics(wall_clock), lists:foreach(fun(Pid) -> Pid ! die end, L), 
   
   U1 = Time1 * 1000 / N, 
   U2 = Time2 * 1000 / N, 
   io:format("Process spawn time=~p (~p) microseconds~n" , [U1, U2]).
   wait() -> 
   
   receive 
      die -> void 
   end. 
 
for(N, N, F) -> [F()]; 
for(I, N, F) -> [F()|for(I+1, N, F)]. 

start()->
   max(1000), 
   max(100000).

Trên bất kỳ máy nào có sức mạnh xử lý tốt, cả hai chức năng tối đa trên sẽ vượt qua. Sau đây là kết quả mẫu từ chương trình trên.

Maximum allowed processes:262144
Process spawn time=47.0 (16.0) microseconds
Maximum allowed processes:262144
Process spawn time=12.81 (10.15) microseconds

Nhận với thời gian chờ

Đôi khi một câu lệnh nhận có thể chờ đợi mãi mãi cho một tin nhắn không bao giờ đến. Điều này có thể là vì một số lý do. Ví dụ: có thể có một lỗi lôgic trong chương trình của chúng tôi hoặc quá trình sẽ gửi cho chúng tôi một tin nhắn có thể đã bị lỗi trước khi nó gửi tin nhắn. Để tránh sự cố này, chúng ta có thể thêm thời gian chờ vào câu lệnh nhận. Điều này đặt thời gian tối đa mà quá trình sẽ đợi để nhận được tin nhắn.

Sau đây là cú pháp của tin nhắn nhận được với thời gian chờ được chỉ định

Cú pháp

receive 
Pattern1 [when Guard1] -> 
Expressions1; 

Pattern2 [when Guard2] ->
Expressions2; 
... 
after Time -> 
Expressions 
end

Ví dụ đơn giản nhất là tạo một hàm sleeper như trong chương trình sau.

Thí dụ

-module(helloworld). 
-export([sleep/1,start/0]). 

sleep(T) ->
   receive 
   after T -> 
      true 
   end. 
   
start()->
   sleep(1000).

Đoạn mã trên sẽ ngủ trong 1000 Ms trước khi thực sự thoát ra.

Nhận có chọn lọc

Mỗi quy trình trong Erlang có một hộp thư liên kết. Khi bạn gửi tin nhắn đến quy trình, tin nhắn sẽ được đưa vào hộp thư. Lần duy nhất hộp thư này được kiểm tra là khi chương trình của bạn đánh giá một câu lệnh nhận.

Sau đây là cú pháp chung của câu lệnh Nhận chọn lọc.

Cú pháp

receive 
Pattern1 [when Guard1] ->
Expressions1; 

Pattern2 [when Guard1] ->
Expressions1; 
... 
after 
Time ->
ExpressionTimeout 
end

Đây là cách hoạt động của câu lệnh nhận ở trên -

  • Khi chúng tôi nhập một câu lệnh nhận, chúng tôi bắt đầu một bộ đếm thời gian (nhưng chỉ khi phần sau có trong biểu thức).

  • Lấy thư đầu tiên trong hộp thư và thử đối sánh nó với Pattern1, Pattern2, v.v. Nếu đối sánh thành công, thư sẽ bị xóa khỏi hộp thư và các biểu thức sau mẫu được đánh giá.

  • Nếu không có mẫu nào trong câu lệnh nhận khớp với thư đầu tiên trong hộp thư, thì thư đầu tiên sẽ bị xóa khỏi hộp thư và đưa vào “hàng đợi lưu”. Thư thứ hai trong hộp thư sau đó được thử. Quy trình này được lặp lại cho đến khi tìm thấy một thư phù hợp hoặc cho đến khi tất cả các thư trong hộp thư đã được kiểm tra.

  • Nếu không có thư nào trong hộp thư trùng khớp, thì quá trình sẽ bị tạm ngừng và sẽ được lên lịch lại để thực hiện vào lần tiếp theo một thư mới được đưa vào hộp thư. Lưu ý rằng khi có thư mới, các thư trong hàng đợi lưu sẽ không được gửi lại; chỉ có tin nhắn mới được khớp.

  • Ngay sau khi một thư đã được đối sánh, thì tất cả các thư đã được đưa vào hàng đợi lưu sẽ được chuyển lại vào hộp thư theo thứ tự mà chúng đến trong quá trình. Nếu bộ hẹn giờ đã được đặt, nó sẽ bị xóa.

  • Nếu bộ đếm thời gian trôi qua khi chúng ta đang đợi một tin nhắn, thì hãy đánh giá biểu thức ExpressionsTimeout và đặt mọi tin nhắn đã lưu trở lại hộp thư theo thứ tự mà chúng đến trong quá trình.