Gần đây tôi đã hỏi một số câu hỏi liên quan đến TVar
và tôi vẫn lo ngại về livelock.Cấu trúc dữ liệu chung đồng thời mà không bị deadlocks hoặc mất tài nguyên
Vì vậy, tôi nghĩ về cấu trúc này:
- Mỗi giao dịch được ưu tiên duy nhất (có lẽ được phân bổ theo thứ tự tạo).
- Giao dịch cố gắng lấy khóa đọc/ghi trên dữ liệu mà họ truy cập. Đương nhiên, đọc đồng thời là okay, nhưng một khóa viết không bao gồm tất cả những người khác (cả đọc và viết).
- Nói giao dịch A có ưu tiên cao hơn giao dịch B. Nếu A giữ khóa, B đợi, nhưng nếu B giữ khóa và A muốn, B sẽ khởi động từ khóa, A lấy nó và giao dịch B khởi động lại (như với
TVar
). Tuy nhiên, B vẫn giữ mức độ ưu tiên hiện tại cho việc thử lại. - Khi khóa được giải phóng và có các giao dịch đang chờ, nó sẽ chuyển đến giao dịch ưu tiên cao nhất và phần còn lại tiếp tục chờ.
Hệ thống này tôi tin rằng ngăn chặn deadlocks, nhưng cũng ngăn chặn nạn đói (không giống như TVar
). Tôi đã tự hỏi nếu có ai đã thực hiện một hệ thống như vậy, vì nó có vẻ khá rõ ràng và tôi không muốn phát minh lại bánh xe.
Tất nhiên, cách tiếp cận này có thể dễ dàng được mở rộng để cho phép người dùng chỉ định mức độ ưu tiên.
Ưu tiên có thể là cặp (user_supplied_prio, auto_increment)
, với mức ưu tiên là user_supplied_prio
, nhưng các tác vụ ưu tiên bằng nhau sẽ được giải quyết theo thứ tự FIFO.
Comment/Giải pháp:
Trên thực tế, khi tôi nghĩ về nó, những gì tôi mô tả đã tồn tại trong Haskell, chỉ đơn giản bằng cách sử dụng một IORef
quấn quanh tất cả các dữ liệu, và chỉ sử dụng atomicModifyIORef
. atomicModifyIORef
sẽ đảm bảo các giao dịch được áp dụng theo thứ tự. Tuy nhiên, người ta có thể nghĩ rằng điều này có nghĩa là cấu trúc dữ liệu được tuần tự (tức là giới hạn hiệu quả cho một luồng) nhưng nó thực sự song song do sự lười biếng.
Để giải thích điều này, hãy xem xét hàm đắt tiền f
. Chúng tôi sẽ áp dụng điều này cho một số Data.Map
đến dữ liệu có khóa "foo".
Điều này thay thế (foo -> x)
bằng (foo -> future(f x))
. Chủ đề này sẽ tiếp tục tìm ra những gì thực tế là (f x)
, nhưng trong khi chờ đợi chúng ta có thể áp dụng g cho "bar". Vì áp dụng g vào "bar" không cần kết quả của "foo", chúng tôi có thể làm việc này cùng một lúc.
Không có deadlocks, không có đói, cuối cùng tất cả các giao dịch sẽ được xử lý (khoảng theo thứ tự nhận được).
Tôi không biết bất kỳ hệ thống nào tồn tại trong Haskell. Dường như quá phức tạp đối với hầu hết người dùng và khá khó thực hiện. Đặc biệt, vô hiệu hóa một khóa được chỉ định sẽ yêu cầu bỏ phiếu (hơi tẻ nhạt với mã) hoặc ngoại lệ asynch (một toàn bộ các sâu khác). Tôi sẽ đề nghị bạn chỉ cần thử STM để thực hiện của bạn và xem nó hoạt động như thế nào; Các thuật toán STM thường đủ đơn giản để nó không phải là một khoản đầu tư thời gian đáng kể nếu bạn cần thay thế nó. –
Phát biểu từ quan điểm STM, việc thêm cơ chế ưu tiên vào thời gian chạy (tương tự như cơ chế bạn đã đề cập dựa trên ngày) sẽ giải quyết vấn đề về livelock mà tôi tin. Tuy nhiên, nó có thể hạn chế nghiêm trọng các lựa chọn của trình lên lịch, mà thậm chí có thể là lý do tại sao không có cơ chế như vậy trong thời gian chạy STM hiện tại. PS: bạn có thể muốn đánh dấu một số câu trả lời cho câu hỏi trước của mình là "câu trả lời được chấp nhận", để cho cộng đồng biết liệu câu hỏi đã được giải quyết chưa. – Peter
Yêu cầu về hiệu suất là gì? Bạn có thể có được những gì bạn muốn thông qua các phương tiện đủ hạt thô như khóa vé toàn cầu (http://en.wikipedia.org/wiki/Ticket_lock), hoặc bằng cách enqueueing tất cả các hành động được thực thi serially bởi một sợi đơn. Các phương thức đồng bộ hóa tinh vi có xu hướng có chi phí cao hơn.Nếu đồng bộ hóa toàn cầu không phải là một nút cổ chai cho một khối lượng công việc nhất định, nó có thể nhanh hơn. – Heatsink