Đồng thời có thể kết hợp trong Java hoặc bất kỳ ngôn ngữ lập trình nào khác


8

Trong khi tôi đang đọc một bài nghiên cứu về đồng thời có tên Phần mềm và Cuộc cách mạng đồng thời ( phiên bản html ). Tôi đã xem qua các dòng sau:

Thật không may, mặc dù các khóa hoạt động, chúng gây ra các vấn đề nghiêm trọng cho phát triển phần mềm hiện đại. Một vấn đề cơ bản với ổ khóa là chúng không thể ghép lại được . Bạn không thể lấy hai đoạn mã dựa trên khóa chính xác, kết hợp chúng và biết rằng kết quả vẫn đúng. Phát triển phần mềm hiện đại phụ thuộc vào khả năng kết hợp các thư viện thành các chương trình lớn hơn và do đó, một khó khăn nghiêm trọng là chúng ta không thể xây dựng trên các thành phần dựa trên khóa mà không kiểm tra việc triển khai của chúng.

  1. Tôi đã suy nghĩ, làm thế nào Java đảm bảo đồng thời có thể kết hợp hoặc thậm chí có một cách để tạo ra các kịch bản này.

  2. Và làm thế nào chúng ta có thể đồng bộ hóa dữ liệu trong một hoặc nhiều thư viện? Một lập trình viên có thể làm điều đó từ chương trình của mình hoặc tùy thuộc vào thư viện để đồng bộ hóa mọi thứ.

  3. Nếu không phải Java thì có ngôn ngữ nào khác sử dụng đồng thời dựa trên khóa và đảm bảo đồng thời có thể kết hợp được không?

Sau đây cũng được lấy từ cùng một tờ giấy:

Có ít nhất ba vấn đề chính với các phương thức đồng bộ. Đầu tiên, chúng không phù hợp với các loại có phương thức gọi các hàm ảo trên các đối tượng khác (ví dụ: Vector's Vector và .NET's SyncHashTable), bởi vì việc gọi vào mã của bên thứ ba trong khi giữ khóa sẽ mở ra khả năng bế tắc . Thứ hai, các phương thức được đồng bộ hóa có thể thực hiện quá nhiều khóa, bằng cách thu thập và giải phóng các khóa trên tất cả các trường hợp đối tượng, ngay cả những phương thức không bao giờ được chia sẻ qua các luồng (thường là đa số). Thứ ba, các phương thức được đồng bộ hóa cũng có thể thực hiện quá ít khóa, bằng cách không bảo toàn tính nguyên tử khi một chương trình gọi nhiều phương thức trên một đối tượng hoặc trên các đối tượng khác nhau. Như một ví dụ đơn giản về sau, hãy xem xét chuyển khoản ngân hàng: account1.Credit (số tiền); tài khoản2.Debit (số tiền) ...

Lưu ý: Giấy được xuất bản vào tháng 9 năm 2005



Tài liệu @ErikEidt có 94 Trích dẫn

3
Không phải là một câu trả lời cho câu hỏi, nhưng bạn sẽ có được sự tương tranh tốt hơn nhiều khi bạn hạn chế các tương tác chuyển qua tin nhắn: Các quy trình (quy trình đầy đủ, không chỉ các luồng) giao tiếp qua hàng đợi tin nhắn không đồng bộ. Đúng, nó không hoạt động như mô hình dựa trên mối đe dọa, nhưng nếu việc gửi tin nhắn không thể chặn, bạn không thể bế tắc trừ khi bạn có một phụ thuộc dữ liệu vòng tròn thực sự được mã hóa vào tin nhắn của bạn. Quan trọng nhất, bất kỳ thông điệp nào cũng được sáng tác / diễn giải tầm thường như một hoạt động nguyên tử, vì vậy tất cả các vấn đề đau đầu của trạng thái nhất quán đều biến mất.
cmaster - phục hồi monica

1
Bộ nhớ giao dịch phần mềm là một cấu trúc tương tranh có thể ghép lại được. Haskell có một thư viện STM.
sắp tới

1
@penguin: đồng thời! = song song. Javascript (node.js) là một ví dụ điển hình về ngôn ngữ có độ đồng thời rất cao (thực sự tất cả I / O đều đồng thời) nhưng là một luồng đơn. Java có khuôn khổ khá tốt cho việc này dưới dạng Tương lai (CompleteionStage). Tất nhiên, làm thế nào khóa mã của bạn phụ thuộc vào việc triển khai nhưng các công cụ có sẵn để bạn phát triển.
slebetman ngày

Câu trả lời:


7

Đó không phải là ngôn ngữ Java. Đó là bản chất của ổ khóa (mutexes).

Có nhiều cách tốt hơn để cải thiện đồng thời trong khi vẫn đảm bảo tính chính xác, những cách độc lập với ngôn ngữ:

  1. Sử dụng các đối tượng bất biến, do đó bạn không cần khóa.
  2. Sử dụng lời hứa và phong cách tiếp tục vượt qua.
  3. Sử dụng cấu trúc dữ liệu không khóa.
  4. Sử dụng bộ nhớ giao dịch phần mềm để chia sẻ trạng thái an toàn.

Tất cả các kỹ thuật này cho phép cải thiện đồng thời mà không cần sử dụng khóa. Không ai trong số họ phụ thuộc vào ngôn ngữ Java một cách cụ thể.


3

Tôi đã suy nghĩ, làm thế nào Java đảm bảo đồng thời có thể kết hợp hoặc thậm chí có một cách để tạo ra các kịch bản này.

Như bài viết đã nói, không thể đảm bảo khả năng kết hợp khi sử dụng khóa cùng với các phương thức ảo (hoặc cơ chế tương tự khác, như truyền các hàm làm tham số). Nếu một đoạn mã có quyền truy cập vào các phương thức ảo đến từ một đoạn mã khác và cả hai đều có khả năng sử dụng các khóa, thì để an toàn (tức là không có nguy cơ bế tắc), hãy soạn hai đoạn mã, bạn cần kiểm tra mã nguồn của cả hai.

Và làm thế nào chúng ta có thể đồng bộ hóa dữ liệu trong một hoặc nhiều thư viện? Một lập trình viên có thể làm điều đó từ chương trình của mình hoặc tùy thuộc vào thư viện để đồng bộ hóa mọi thứ.

Nói chung, tùy thuộc vào lập trình viên sử dụng các thư viện để thực hiện đồng bộ hóa. Bằng cách đó, lập trình viên biết tất cả các khóa đang ở đâu và họ có thể đảm bảo rằng chúng không bế tắc.

Nếu không phải Java thì có ngôn ngữ nào khác sử dụng đồng thời dựa trên khóa và đảm bảo đồng thời có thể kết hợp được không?

Một lần nữa, quan điểm của bài viết là điều này là không thể.


Bài viết đã được xuất bản năm 2005. Câu trả lời của bạn dựa trên cùng một bài viết. Ngôn ngữ đã phát triển rất nhiều kể từ năm 2005. Bạn có thể đưa ra bất kỳ tài liệu tham khảo mới nhất nào không hoặc bạn có thể xác nhận lại câu trả lời của mình không.

2
Bài báo đó không nói về bất cứ điều gì cụ thể cho năm 2005. Nó vẫn đúng trong năm 2105 rằng các thư viện dựa trên khóa không thể ghép lại được.
Svick

Java liên tục cập nhật các cấu trúc khóa của nó, bạn có thể so sánh gói đồng thời Java trong Java5 với Java8.

Không quan trọng. Java 8 chắc chắn đã không loại bỏ khả năng bế tắc khi sử dụng các khóa, vì vậy không có gì thay đổi về điều này.
Svick

2
@penguin - Không thể tạo ra một ngôn ngữ hoàn toàn có thể ghép được một khi nó có các tính năng không thể kết hợp được, bởi vì bạn không thể đảm bảo rằng các mô-đun đóng không sử dụng chúng theo cách nguy hiểm. Bạn có thể làm cho mã của mình có thể kết hợp được, ngay cả khi bạn đang sử dụng khóa (mặc dù bạn phải cẩn thận về cách bạn sử dụng chúng trong trường hợp này), nhưng bạn không thể chắc chắn rằng các thư viện và / hoặc các thành phần hệ thống tùy ý không.
Jules

1

Các cơ chế khóa cấp thấp vốn không thể ghép lại được. Điều này chủ yếu là do các khóa tiếp cận bên dưới thế giới ảnh hưởng đến máy thực hiện các hướng dẫn.

Các thư viện Java tiếp theo đã thêm các cơ chế cấp cao hơn và cao hơn để đảm bảo hoạt động đa luồng chính xác. Họ làm điều này bằng cách hạn chế việc sử dụng lock()volatiletrong một số trường hợp nổi tiếng và có thể kiểm soát được. Ví dụ, việc thực hiện hàng đợi đồng thời có hành vi rất cục bộ và cho phép suy luận về các trạng thái trước và sau. Sử dụng các cơ chế cấp cao hơn có nghĩa là bạn cần đọc ít thông số kỹ thuật hoặc mã hơn để làm cho đúng. Nhưng, và đây là một vấn đề lớn, nhưng bạn vẫn cần hiểu mô hình khóa của bất kỳ hệ thống con nào và cách chúng tương tác với nhau. Ngoài ra, các thay đổi trong Java cho đồng thời sau Java 5 hầu như chỉ liên quan đến các thư viện chứ không phải ngôn ngữ.

Vấn đề chính với bất kỳ cơ chế khóa nào là nó ảnh hưởng đến trạng thái và hoạt động trong miền thời gian. Cả con người và máy tính đều không có lý do tốt về trạng thái hoặc thời gian. Đó là khả năng suy luận về giá trị và cấu trúc cho phép các nhà khoa học máy tính đưa ra các đơn nguyên, điều đầu tiên tôi nghĩ đến về khả năng kết hợp trong ngôn ngữ.

Gần nhất chúng tôi đã đến là Giao tiếp các quy trình tuần tự . Điều này vẫn đòi hỏi một cơ chế cấp cao, chẳng hạn như hộp thư và tin nhắn đi qua. Theo ý kiến ​​khiêm tốn của tôi, CSP vẫn không giải quyết thỏa đáng với các hệ thống lớn (mục tiêu cuối cùng của phần mềm có thể kết hợp) hoặc lý luận dựa trên thời gian.


1
Điều này không trả lời câu hỏi của tôi. Tôi muốn biết liệu Java có thể kết hợp được không hoặc có ngôn ngữ nào khác không? Theo hai câu trả lời tôi nhận được, Java không thể kết hợp được nhưng cả hai câu trả lời đều không cung cấp bất kỳ bằng chứng cụ thể nào.

Bạn có muốn một bằng chứng rằng Java chỉ sử dụng các khóa không thể kết hợp được không? Ngoài ra, nếu bạn đọc bài viết CSP ở trên, bạn sẽ thấy một loạt các ngôn ngữ được đề cập có thể ghép lại được. Nhận xét của tôi đề cập đến bất kỳ ngôn ngữ nào lock()volatilelà mức độ chi tiết của luồng hoặc đồng bộ hóa quy trình.
BobDalgleish

Được rồi, có một cái gì đó tôi quan tâm. Trong câu trả lời của bạn, bạn đã nói "Gần nhất chúng ta đã đến ..." ý của bạn là gì. Làm thế nào bạn tìm ra CSP là gần nhất?

1

Trước hết tôi cảm ơn tất cả các thành viên đã trả lời câu hỏi này, đặc biệt là Robert Harvey có câu trả lời rất gần với tôi.

Tôi đã nghiên cứu về các khái niệm đồng thời trong hai năm và theo phát hiện của tôi, không có ngôn ngữ nào đảm bảo rằng các cấu trúc đồng thời của nó có thể ghép được. Mã chạy hoàn hảo tốt sử dụng cấu trúc dữ liệu bất biến và STM cũng có thể tạo ra kết quả không mong muốn vì dưới mui xe, STM sử dụng khóa. STM rất tốt cho các hoạt động nguyên tử, nhưng nếu chúng ta nói về khả năng tương thích đồng thời giữa các mô-đun, có khả năng (rất nhẹ) rằng STM có thể không hoạt động như mong đợi.

Tuy nhiên, chúng ta vẫn có thể giảm thiểu sự không chắc chắn bằng cách sử dụng (kỹ thuật / phương pháp / cấu trúc) sau đây:

  1. Tránh khóa và đồng bộ hóa
  2. Sử dụng STM
  3. Sử dụng cấu trúc dữ liệu liên tục (không thay đổi)
  4. Tránh chia sẻ trạng thái
  5. Hàm tinh khiết
  6. Phong cách lập trình không đồng bộ
  7. Tránh chuyển đổi ngữ cảnh thường xuyên
  8. Mô hình đa luồng và bản chất của môi trường đóng vai trò chính

Có lẽ sự phản đối cơ bản nhất [...] là các chương trình dựa trên khóa không sáng tác: các đoạn chính xác có thể thất bại khi kết hợp. TiếtTim Harris và cộng sự, "Giao dịch bộ nhớ tổng hợp", Phần 2: Bối cảnh, trg.2

Cập nhật

Nhờ Jules, tôi đứng chính xác. STM sử dụng các triển khai khác nhau và hầu hết trong số chúng là khóa miễn phí. Nhưng tôi vẫn tin rằng STM là một giải pháp tốt ở đây, nhưng không phải là giải pháp hoàn hảo và nó có nhược điểm:

Mỗi lần đọc (hoặc ghi) của một vị trí bộ nhớ từ bên trong một giao dịch được thực hiện bằng một cuộc gọi đến một thói quen STM để đọc (hoặc ghi) dữ liệu. Với mã tuần tự, các truy cập này được thực hiện bởi một lệnh CPU. Các thói quen đọc và ghi STM đắt hơn đáng kể so với các hướng dẫn CPU tương ứng vì thông thường, chúng phải duy trì dữ liệu sổ sách kế toán về mọi truy cập. Hầu hết các STM kiểm tra xung đột với các giao dịch đồng thời khác, ghi nhật ký truy cập và trong trường hợp ghi, ghi lại giá trị hiện tại (hoặc cũ) của dữ liệu, ngoài việc đọc hoặc ghi vị trí bộ nhớ đã truy cập. Một số thao tác này sử dụng các hướng dẫn đồng bộ hóa đắt tiền và truy cập siêu dữ liệu được chia sẻ, điều này làm tăng thêm chi phí của chúng. Tất cả điều này làm giảm hiệu suất đơn luồng khi so sánh với mã tuần tự. - Tại sao STM có thể nhiều hơn một đồ chơi nghiên cứu - trang 1

Cũng xem các giấy tờ này:

Những giấy tờ này là một vài năm tuổi. Mọi thứ có thể đã thay đổi / cải thiện nhưng không phải tất cả.


"Một mã chạy hoàn toàn tốt sử dụng các cấu trúc dữ liệu bất biến và STM cũng có thể tạo ra kết quả không mong muốn vì STM sử dụng khóa." Bạn có thể cung cấp một ví dụ về một kết quả bất ngờ như vậy? Theo như tôi biết, STM được coi là đáng tin cậy và có thể ghép lại được, nhưng có lẽ bạn biết về điều gì đó mà tôi không ...?
Jules

Ngoài ra, bạn nói rằng STM "sử dụng khóa", nhưng đó chỉ là chi tiết triển khai của một số phiên bản của nó. STM có thể được thực hiện trong một hệ thống hoàn toàn không khóa bằng cách sử dụng các cập nhật nguyên tử; xem dl.acm.org/citation.cfm?id=1941579
Jules

1

Tôi đã nghe các nhà nghiên cứu có uy tín nói rằng bất kỳ cơ chế đồng bộ hóa hữu ích nào cũng có thể được sử dụng để xây dựng một bế tắc. Bộ nhớ giao dịch (cho dù phần cứng hay phần mềm) không khác nhau. Ví dụ, hãy xem xét phương pháp này để viết một rào cản luồng:

transaction {
    counter++;
}

while (true) {
    transaction {
        if (counter == num_threads)
            break;
    }
}

(Lưu ý: ví dụ được lấy từ một bài báo của Yannis Smaragdakis tại PACT 2009)

Nếu chúng ta bỏ qua thực tế rằng đây không phải là một cách tốt để đồng bộ hóa một số lượng lớn các chủ đề, thì nó có vẻ đúng. Nhưng nó không thể ghép lại được. Quyết định đưa logic vào hai giao dịch là điều cần thiết. Nếu chúng ta gọi nó từ một giao dịch khác, sao cho mọi thứ được làm phẳng thành một giao dịch, thì có lẽ chúng ta sẽ không bao giờ hoàn thành.

Điều tương tự cũng đúng với các kênh truyền thông điệp: chu kỳ liên lạc có thể gây ra bế tắc. Đồng bộ hóa đặc biệt với nguyên tử C ++ có thể dẫn đến bế tắc. RCU, khóa trình tự, khóa trình đọc / ghi, biến điều kiện và semaphores đều có thể được sử dụng để tạo bế tắc.

Điều đó không có nghĩa là các giao dịch hoặc kênh (hoặc khóa hoặc RCU) là xấu. Thay vào đó, phải nói rằng một số điều dường như không thể. Các cơ chế kiểm soát tương tranh không có khả năng mở rộng, có thể kết hợp, không có bệnh lý có lẽ là không thể.

Cách tốt nhất để tránh rắc rối không phải là tìm kiếm một cơ chế đạn bạc, mà là sử dụng nghiêm ngặt các mẫu tốt. Trong thế giới của điện toán song song, một điểm khởi đầu tốt là Lập trình song song có cấu trúc: Các mô hình tính toán hiệu quả , bởi Arch Robison, James Reinder và Michael McCool. Đối với lập trình đồng thời, có một số mẫu tốt (xem bình luận của @ gardenenhead), nhưng các lập trình viên C ++ và Java không có khả năng sử dụng chúng. Một mô hình mà nhiều người có thể bắt đầu sử dụng đúng cách là thay thế đồng bộ hóa đặc biệt trong các chương trình bằng hàng đợi nhiều người tiêu dùng. Và TM chắc chắn tốt hơn ổ khóa, ở chỗ nó tăng mức độ trừu tượng, để các lập trình viên tập trung vào những gì cần phải là nguyên tử , không phảilàm thế nào để thực hiện một giao thức khóa thông minh để đảm bảo tính nguyên tử . Hy vọng, khi TM phần cứng được cải thiện và ngôn ngữ thêm hỗ trợ TM, chúng ta sẽ đạt đến điểm mà TM thay thế khóa trong trường hợp phổ biến.


1
"Đối với lập trình đồng thời, các mẫu không được phát triển tốt." Đây là giả dối. Các mô hình đồng thời tính toán trở lại trong nhiều thập kỷ. Mô hình diễn viên được phát minh vào đầu những năm 70, và nhiều hình thức tính toán quy trình khác nhau đã được phát minh và nghiên cứu kể từ thời điểm đó. Vấn đề không nằm ở những mô hình tính toán tốt; nó nằm ở việc các lập trình viên không muốn sử dụng các mô hình đó và sự thiếu hiệu quả có thể xảy ra trong quá trình thực hiện.
vườn

Điểm tuyệt vời, @gardenhead. Các mô hình đã được phát triển, chỉ là không ai sử dụng chúng. Tôi sẽ chỉnh sửa câu trả lời của tôi.
Mike Spear
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.