Nếu tôi sử dụng khóa, thuật toán của tôi vẫn có thể bị khóa?


9

Một định nghĩa phổ biến về khóa miễn phí là ít nhất một quá trình tạo ra tiến bộ. 1

Nếu tôi có cấu trúc dữ liệu đơn giản như hàng đợi, được bảo vệ bởi khóa, thì một quy trình luôn có thể đạt được tiến bộ, vì một quy trình có thể có được khóa, thực hiện những gì nó muốn và giải phóng nó.

Vì vậy, nó đáp ứng định nghĩa của khóa miễn phí?


1 Xem ví dụ: M. Herlihy, V. Luchangco và M. Moir. Đồng bộ hóa không có vật cản: Hàng đợi hai đầu làm ví dụ. Trong Máy tính phân tán, 2003. "Không khóa nếu chỉ đảm bảo rằng một số luồng luôn đạt được tiến bộ".


3
Tôi đã luôn hiểu "khóa miễn phí" để mô tả cấu trúc dữ liệu và bộ thuật toán không sử dụng khóa, chỉ là một tập hợp nhỏ các hoạt động bộ nhớ nguyên tử được xác định. Ví dụ: drdobbs.com/pool/wr-lock-free-code-a-corrected-queue/iêu
Paul Johnson

Câu trả lời:


16

Đó không phải là định nghĩa cho khóa miễn phí.

Nếu bạn có thể đảm bảo tiến độ thì bạn không có bế tắc và nếu bạn hoàn thành tất cả các yêu cầu, thì bạn sẽ không bị đói , nhưng không bị khóa.

Tôi đặt câu hỏi liệu ví dụ đơn giản của bạn có thực sự cung cấp điều này không. Bạn cần phân cấp khóa và cứ thế để thực sự đảm bảo tiến độ khi có nhiều khóa.


1
Tôi đang sử dụng định nghĩa từ M. Herlihy. Một phương pháp để thực hiện các đối tượng dữ liệu đồng thời cao. Giao dịch trên các ngôn ngữ và hệ thống lập trình, 1993. "Điều kiện không khóa đảm bảo rằng một số quy trình sẽ luôn đạt được tiến bộ mặc dù các sự cố tạm dừng hoặc trì hoãn bởi các quy trình khác"
Joe Pension

12
@Joe: Đó không phải là một định nghĩa, nó mô tả một hàm ý. Coi chừng sai lầm logic của nghịch đảo.
Ben Voigt

2
Cũng có thể trích dẫn M. Herlihy, V. Luchangco và M. Moir. Đồng bộ hóa không có vật cản: Hàng đợi hai đầu làm ví dụ. Trong Máy tính phân tán, 2003. "Không khóa nếu chỉ đảm bảo rằng một số luồng luôn đạt được tiến bộ".
Joe Pension

1
Ngoài ra còn có sự chết đói miễn phí, cụ thể hơn là không có bế tắc (mọi quá trình đều diễn ra bất kể các quá trình khác làm gì) lưu ý rằng các vòng lặp CaS (dựa trên các nguyên thủy nguyên tử) không
ratchet freak

5
@Joe: Nếu phần còn lại của thế giới gọi tài sản đó là bế tắc , thì tôi sẽ sử dụng thuật ngữ đó. Không, ví dụ đơn giản của bạn không phải là bế tắc. Để đảm bảo rằng một cái gì đó không có bế tắc, bạn không chỉ cần đồng bộ hóa, mà còn đảm bảo rằng không có luồng nào thực hiện bất kỳ thao tác chặn nào trong khi giữ khóa. "làm những gì nó muốn" là vô cùng mơ hồ và dường như không cung cấp sự đảm bảo này.
Ben Voigt

11

Tôi đã nghiên cứu Nghệ thuật lập trình đa bộ xử lý 1 và văn bản của họ thiếu rõ ràng, giống như cuốn sách bạn đề cập đến. Dưới đây là một số trích dẫn từ TAMPP:

Trích dẫn 1 (Định nghĩa về khóa miễn phí)

Một phương thức không khóa nếu nó đảm bảo rằng vô cùng thường xuyên một số cuộc gọi phương thức kết thúc trong một số bước hữu hạn.

Trích dẫn 2 (Định nghĩa về không chặn)

một yêu cầu đang chờ xử lý của một phương thức tổng thể không bao giờ được yêu cầu để chờ một lệnh gọi đang chờ xử lý khác hoàn thành.

Trích dẫn 3 (tuyên bố rằng khóa miễn phí là không chặn)

Các điều kiện tiến trình không chặn không phải chờ và không khóa đảm bảo rằng toàn bộ tính toán tạo ra tiến trình, độc lập với cách hệ thống lên lịch các luồng.

Vấn đề là yêu cầu trong Trích dẫn 3 rõ ràng không tuân theo định nghĩa trong Trích dẫn 1. Như đã đề cập, một hàng đợi được đồng bộ hóa dường như thỏa mãn Trích dẫn 1: thường thì một số phương pháp sẽ có được thành công khóa và hoàn tất.

Đặc biệt lưu ý cụm từ khá mơ hồ trong Trích dẫn 3: "độc lập với cách hệ thống lên lịch các luồng". Đây không phải là trước bởi bất kỳ loại mô tả chính thức của "hệ thống thread-lịch", vì vậy chúng tôi còn lại để tái tạo thuộc tính của nó dựa trên những định kiến của chúng tôi về những gì các định nghĩa nên có ý nghĩa:

  1. hệ thống luôn thực hiện các hướng dẫn của một số luồng;
  2. nó có thể không bao giờ thực hiện hướng dẫn của bất kỳ cho chủ đề;
  3. tất cả các chủ đề đang gọi phương thức đang xem xét.

Trên một hệ thống như vậy, một phương thức chặn không thể bị khóa: nếu luồng giữ khóa không bao giờ được lên lịch thực hiện lại, sẽ không có luồng nào khác có thể hoàn thành việc gọi phương thức của nó trong một số bước hữu hạn, nhưng sẽ có một số chủ đề đang thực hiện các bước của phương thức. Đối với một hệ thống thực tế hơn, một hệ thống đảm bảo cung cấp thời gian CPU cho từng luồng cuối cùng, định nghĩa phải bao gồm rõ ràng thuộc tính không chặn:

Định nghĩa chính xác của khóa miễn phí

Một phương thức không khóa nếu nó không chặn và, ngoài ra, đảm bảo rằng vô cùng thường xuyên một số cuộc gọi phương thức kết thúc trong một số bước hữu hạn.

1 Maurice Herlihy, Nir Shavit, Nghệ thuật lập trình đa xử lý, Elsevier 2008, trang 58-60


1
Các từ ngữ của trích dẫn 1 là thực sự lạ. Họ có ý nghĩa gì bởi "vô cùng thường xuyên"? Rõ ràng là một cái gì đó khác với "luôn luôn", vậy có ổn không khi phương thức không bao giờ quay trở lại trong trường hợp "một số"?
Hulk

Vâng, rất nhiều ngôn ngữ không chính xác. "Thường" là gì? Tôi nghĩ chúng có nghĩa là "trong một lịch sử thực hiện vô hạn, sự kiện đặc biệt này xảy ra vô hạn nhiều lần".
Marko Topolnik

5

Thuật ngữ không phải lúc nào cũng nhất quán, nhưng tôi nghĩ điều quan trọng là hỏi những câu hỏi sau về một thuật toán hoặc hệ thống được đề xuất:

  1. Có bất kỳ chuỗi sự kiện nào mà các luồng có thể bị kẹt chờ đợi nhau ngay cả khi tất cả các luồng được cho phép tất cả thời gian CPU mà chúng có thể sử dụng [nếu vậy, nó không bị bế tắc]
  2. Nếu một luồng bị chặn trong một thời gian dài tùy ý, điều đó có thể cản trở các luồng khác hoặc làm suy yếu hoạt động của hệ thống trong một thời gian dài tùy ý [nếu vậy, đó không phải là không chặn].
  3. Có một số sự kết hợp ít nhất có thể về mặt lý thuyết của lập lịch xử lý luồng có thể khiến tất cả các luồng liên tục thử lại các hoạt động tương tự trong khi vô hiệu hóa công việc của nhau, mà không có ai tiến triển [nếu vậy, nó không bị khóa]
  4. Nếu một số luồng được cung cấp đủ thời gian CPU so với thời gian khác, thì chúng có thể buộc luồng sau tiếp tục thử lại hoạt động của nó vô thời hạn [nếu vậy, nó không phải chờ đợi].

Phần lớn tầm quan trọng của thuật toán không khóa không phải là chúng nhanh hơn thuật toán không khóa, mà thực tế là chúng không dễ bị chết nếu một luồng được xử lý [lưu ý rằng bảo đảm như vậy chỉ yêu cầu các thuật toán đó không chặn, nhưng tất cả các thuật toán không khóa là]. Thuật toán không khóa có thể sử dụng khóa, nhưng chỉ khi các nỗ lực mua khóa bao gồm hết thời gian chờ cùng với thuật toán để đảm bảo rằng ai đó sẽ luôn có thể tiến bộ (ví dụ: thuật toán có thể sử dụng CompareExchangevòng lặp làm chính phương pháp trọng tài, nhưng sử dụng khóa để phân xử quyền truy cập khi sự tranh chấp có vẻ cao, nếu khóa dường như bị giữ quá lâu, các luồng khác có thể quyết định từ bỏ nỗ lực sử dụng khóa đó và thay vào đó tạo khóa mới.CompareExchange, việc khách hàng từ bỏ khóa sẽ không gây nguy hiểm cho tính nhất quán của hệ thống, mặc dù điều đó có thể có nghĩa là mã đã giữ khóa cũ sẽ không thực hiện được bất kỳ công việc nào cho đến khi nó từ bỏ khóa cũ và phù hợp với khóa mới.


Điều này khác với thuật ngữ tiêu chuẩn: 2. của bạn đề cập đến ý nghĩa tiêu chuẩn của việc không chặn trong khi 3. đề cập đến tự do khóa.
Marko Topolnik

Tôi đã thấy các cách sử dụng thuật ngữ không nhất quán và tôi không biết về một tiêu chuẩn "chính thức". Điều quan trọng nhất là có các đảm bảo khác nhau mà thuật toán có thể cung cấp và điều quan trọng là sử dụng thuật toán cung cấp đảm bảo đủ để đáp ứng các yêu cầu ứng dụng. Nhiều giấy tờ chỉ bao gồm một số bảo đảm ở trên, nhưng có những trường hợp mỗi bảo đảm có thể đáp ứng các yêu cầu ứng dụng dễ dàng hơn bất kỳ bảo đảm nào khác đáp ứng yêu cầu.
supercat

Tôi nghĩ rằng có một sự đồng thuận rằng thuật ngữ được trình bày trong Nghệ thuật lập trình đa bộ xử lý là "tiêu chuẩn".
Marko Topolnik

@MarkoTopolnik: Tôi sẽ chỉnh sửa bài để phù hợp với điều đó sau đó. Bạn có thích phiên bản mới không?
supercat

Mát mẻ, rất đẹp.
Marko Topolnik

4

Bạn phải xem "định nghĩa" bạn trích dẫn trong ngữ cảnh :

Cách truyền thống để thực hiện các cấu trúc dữ liệu được chia sẻ là sử dụng loại trừ lẫn nhau (khóa) để đảm bảo rằng các hoạt động đồng thời không can thiệp lẫn nhau. Khóa có một số nhược điểm liên quan đến kỹ thuật phần mềm, khả năng chịu lỗi và khả năng mở rộng (xem [8]). Đáp lại, các nhà nghiên cứu đã điều tra một loạt các kỹ thuật đồng bộ hóa thay thế không sử dụng loại trừ lẫn nhau . Một kỹ thuật đồng bộ hóa là chờ đợi nếu nó đảm bảo rằng mọi luồng sẽ tiếp tục đạt được tiến bộ khi đối mặt với độ trễ tùy ý (hoặc thậm chí là thất bại) của các luồng khác. Nó là khóa nếu nó chỉ đảm bảo rằng một số luồng luôn luôn tiến bộ.

Bạn đang sử dụng khóa để loại trừ lẫn nhau, do đó đây không phải là kỹ thuật không khóa mà họ đang nói đến.


2

Nếu tôi sử dụng khóa, thuật toán của tôi vẫn có thể bị khóa?

Nó có thể, nhưng nó phụ thuộc vào thuật toán.

Nếu tôi có cấu trúc dữ liệu đơn giản như hàng đợi, được bảo vệ bởi khóa, thì một quy trình luôn có thể đạt được tiến bộ, vì một quy trình có thể có được khóa, thực hiện những gì nó muốn và giải phóng nó.

Vì vậy, nó đáp ứng định nghĩa của khóa miễn phí?

Lưu ý mỗi se .

Nếu bước "làm những gì nó muốn" không liên quan đến việc có được bất kỳ khóa nào khác và nó được đảm bảo hoàn thành trong một thời gian hữu hạn, thì phần đặc biệt này trong thuật toán của bạn sẽ không bị bế tắc.

Tuy nhiên, nếu những điều kiện tiên quyết đó không được thỏa mãn, ít nhất có khả năng xảy ra bế tắc ...


Sau một số nghiên cứu về văn bản trong Nghệ thuật lập trình đa bộ xử lý, tôi đã đi đến kết luận rằng các trường hợp đột biến làm mất hiệu lực định nghĩa về khóa không khóa, khi định nghĩa được viết đúng. Tôi đã thêm một câu trả lời cho trang này để làm rõ điều này.
Marko Topolnik

1

Ví dụ bạn đưa ra không khóa vì lý do sau.

Hỗ trợ một luồng thu được khóa và bộ lập lịch hệ điều hành đã treo luồng trong một khoảng thời gian đơn lẻ vô hạn, sau đó tất cả các luồng không thể thực hiện tiến trình vì không thể có được khóa được lấy bởi luồng bị treo.

Nói chung, các thuật toán sử dụng khóa không phải là khóa.

Lưu ý rằng deadlock-free và lock-free là hai khái niệm khác nhau. không có bế tắc có nghĩa là không có khả năng cho sự bế tắc, nhưng có thể có sự thay đổi có thể ngăn chặn toàn bộ hệ thống đạt được tiến bộ. Khóa tự do mạnh hơn thế bởi vì điều đó có nghĩa là một số luồng trong hệ thống luôn đạt được tiến bộ với số bước hữu hạn.


Nhìn vào một định nghĩa cẩn thận hơn trên Wikipedia: "Một thuật toán không bị khóa nếu nó thỏa mãn rằng khi các luồng chương trình được chạy đủ lâu thì ít nhất một trong số các luồng sẽ tiến triển." Điều này không bao gồm các trường hợp tạm dừng chủ đề. Ngoài ra tiến trình tạm dừng được bao phủ bởi tự do cản trở , không khóa tự do .
Marko Topolnik

@MarkoTopolnik Nhận xét của bạn không có ý nghĩa gì cả. Khóa tự do bao gồm cản trở tự do. Bất cứ thứ gì không có khóa đều phải không có vật cản. Và định nghĩa bạn đưa ra không loại trừ các chủ đề tạm dừng.
Chaoran

Hãy cẩn thận để phân biệt "có ý nghĩa" với "chính xác". Nhận xét của tôi là không chính xác, như có thể được nhìn thấy từ câu trả lời tiếp theo của tôi. Nhưng định nghĩa Wikipedia cũng sai hoặc ít nhất là mơ hồ.
Marko Topolnik

@MarkoTopolnik Vì bạn thừa nhận rằng nhận xét của bạn không đúng, bạn nên xóa nó để tránh gây nhầm lẫn cho những người đọc khác. Wikipedia thường không chính xác hoặc mơ hồ. Bạn nên tìm định nghĩa tinh tế như "khóa tự do" trong giấy tờ học tập như cs.rochester.edu/~scott/papers/2006_PPoPP_synch_queues.pdf (định nghĩa của là lock-free trong phần 2.1)
Chaoran

Có, bao gồm cả thuộc tính không chặn như là một phần của định nghĩa về tự do khóa là một cách để làm điều đó. Điều đó đã được nêu trong một bản sửa đổi trước đó của câu trả lời của tôi.
Marko Topolnik
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.