Tôi biết rằng có quá trình GenServer không thể tự gọi được vì bạn về cơ bản bị bế tắc. Nhưng, tôi tò mò nếu có một cách ưa thích để làm điều này.Cách thích hợp để cấu trúc các cuộc gọi GenServer tới chính mình
Giả sử trường hợp sau: Tôi có hàng đợi rằng tôi đang popping mọi thứ. Nếu hàng đợi trống, tôi muốn nạp nó lại. Tôi có thể cấu trúc nó như vậy:
def handle_call(:refill_queue, state) do
new_state = put_some_stuff_in_queue(state)
{:reply, new_state}
end
def handle_call(:pop, state) do
if is_empty_queue(state) do
GenServer.call(self, :refill_queue)
end
val,new_state = pop_something(state)
{:reply, val, new_state}
end
Vấn đề lớn ở đây là điều này sẽ bế tắc khi chúng tôi cố nạp lại hàng đợi. Một giải pháp mà tôi đã sử dụng trong quá khứ là sử dụng cast
nhiều hơn để nó không bế tắc. Như vậy (thay đổi call
để cast
cho refill)
def handle_cast(:refill_queue, state) do
Nhưng trong trường hợp này, tôi nghĩ rằng nó sẽ không làm việc, kể từ khi các diễn viên async để nạp tiền hàng đợi có thể trở lại trong trường hợp pop
trước khi thực sự làm đầy ý nghĩa hàng đợi Tôi sẽ cố gắng bật ra một hàng trống.
Dù sao, câu hỏi cốt lõi là: Cách tốt nhất để xử lý vấn đề này là gì? Tôi cho rằng câu trả lời là chỉ gọi put_some_stuff_in_queue
trực tiếp trong cuộc gọi pop
, nhưng tôi muốn kiểm tra. Nói cách khác, có vẻ như việc phải làm là làm cho handle_call
và handle_cast
càng đơn giản càng tốt và về cơ bản chỉ là trình bao bọc cho các chức năng khác nơi công việc thực sự xảy ra. Sau đó, tạo nhiều chức năng handle_*
khi bạn cần bao gồm tất cả các trường hợp có thể xảy ra, thay vì có handle_call(:foo)
lần lượt gọi handle_call(:bar)
.
Tôi sẽ làm cho 'refill_queue' trở thành một hàm đơn giản và gọi nó từ' handle_call (: pop) 'nếu bạn cần nó đồng bộ. Nếu không, bạn có một số tùy chọn để xử lý async (gửi một thông điệp khác đến 'self', có một quá trình xử lý nạp lại, vv) –
Bạn nên xem [' GenStage'] (http://elixir-lang.org/blog/2016/07/14/thông báo-genstage /), có vẻ như nó cung cấp chức năng bạn đang tìm kiếm. – mudasobwa