Ý nghĩa của thuật ngữ này là gì


367

Có nghĩa là hai luồng không thể thay đổi dữ liệu cơ bản cùng một lúc? Hoặc nó có nghĩa là đoạn mã đã cho sẽ chạy với kết quả có thể dự đoán được khi nhiều luồng đang thực thi đoạn mã đó?


8
Chỉ cần thấy một cuộc thảo luận thú vị ở đây về vấn đề này: blog.msdn.com/ericlippert/archive/2009/10/19/NH
Sebastian

Câu trả lời:


256

Từ Wikipedia:

An toàn luồng là một khái niệm lập trình máy tính áp dụng trong ngữ cảnh của các chương trình đa luồng. Một đoạn mã là an toàn luồng nếu nó hoạt động chính xác trong khi thực hiện đồng thời bởi nhiều luồng. Cụ thể, nó phải đáp ứng nhu cầu cho nhiều luồng truy cập vào cùng một dữ liệu được chia sẻ và nhu cầu về một phần dữ liệu được chia sẻ chỉ được truy cập bởi một luồng tại bất kỳ thời điểm nào.

Có một số cách để đạt được an toàn luồng:

Nhập lại:

Viết mã theo cách mà nó có thể được thực thi một phần bởi một tác vụ, được nhập lại bởi một tác vụ khác và sau đó được tiếp tục từ tác vụ ban đầu. Điều này đòi hỏi phải lưu thông tin trạng thái trong các biến cục bộ cho từng tác vụ, thường là trên ngăn xếp của nó, thay vì trong các biến tĩnh hoặc toàn cục.

Loại trừ lẫn nhau:

Truy cập vào dữ liệu chia sẻ được tuần tự hóa bằng các cơ chế đảm bảo chỉ có một luồng đọc hoặc ghi dữ liệu được chia sẻ bất cứ lúc nào. Cần hết sức cẩn thận nếu một đoạn mã truy cập vào nhiều đoạn dữ liệu được chia sẻ Các vấn đề về giáo dục bao gồm các điều kiện chủng tộc, bế tắc, sinh động, chết đói và nhiều bệnh khác được liệt kê trong nhiều sách giáo khoa của hệ điều hành.

Lưu trữ theo luồng cục bộ:

Các biến được bản địa hóa để mỗi luồng có bản sao riêng. Các biến này giữ giá trị của chúng trên chương trình con và các ranh giới mã khác và an toàn cho luồng vì chúng là cục bộ của từng luồng, mặc dù mã truy cập chúng có thể được cấp lại.

Hoạt động nguyên tử:

Dữ liệu được chia sẻ được truy cập bằng cách sử dụng các hoạt động nguyên tử không thể bị gián đoạn bởi các luồng khác. Điều này thường yêu cầu sử dụng các hướng dẫn ngôn ngữ máy đặc biệt, có thể có sẵn trong thư viện thời gian chạy. Vì các hoạt động là nguyên tử, dữ liệu chia sẻ luôn được giữ ở trạng thái hợp lệ, bất kể các luồng khác truy cập vào nó. Hoạt động nguyên tử tạo thành cơ sở của nhiều cơ chế khóa luồng.

đọc thêm:

http://en.wikipedia.org/wiki/Thread_safe



4
Liên kết này, về mặt kỹ thuật, thiếu một số điểm quan trọng. Bộ nhớ dùng chung trong câu hỏi phải có thể thay đổi, (Bộ nhớ chỉ đọc không thể là luồng không an toàn) và nhiều luồng phải, a) thực hiện nhiều thao tác ghi trên bộ nhớ trong khi bộ nhớ không nhất quán (sai) trạng thái và b) cho phép các luồng khác làm gián đoạn luồng đó trong khi bộ nhớ không nhất quán.
Charles Bretana

20
Khi tìm kiếm kết quả đầu tiên của Google là wiki, không có điểm nào để làm cho nó dư thừa ở đây.
Ranvir

Bạn có ý nghĩa gì "một mã truy cập một chức năng"? Hàm được thực thi chính nó là mã, phải không?
Koray Tugay

82

Mã an toàn chủ đề là mã sẽ hoạt động ngay cả khi nhiều Chủ đề đang thực thi đồng thời.

http://mindprod.com/jgloss/threadsafe.html


34
Trong cùng một quá trình!
Ali Afshar

Thật vậy, trong cùng một quá trình :)
Marek Blotny

4
"Để viết mã sẽ chạy ổn định trong nhiều tuần mất nhiều hoang tưởng." Đó là một câu trích dẫn mà tôi thích :)
Jim T

5
tât nhiên! Câu trả lời này chỉ cần đặt lại câu hỏi! --- Và tại sao chỉ trong cùng một quy trình ??? Nếu mã không thành công khi nhiều luồng thực thi nó từ các quy trình khác nhau, thì có thể cho rằng ("bộ nhớ dùng chung" có thể nằm trong một tệp đĩa), nó KHÔNG phải là luồng an toàn !!
Charles Bretana

1
@ mg30rg. Có lẽ sự nhầm lẫn là kết quả của việc bằng cách nào đó nghĩ rằng khi một khối mã được thực thi bởi nhiều quy trình, nhưng chỉ bởi một luồng trên mỗi quy trình, thì bằng cách nào đó, đó vẫn là một kịch bản "Một luồng", không phải là một kịch bản đa luồng . Ý tưởng này thậm chí không sai. Nó chỉ là định nghĩa sai. Rõ ràng, nhiều quy trình thường không thực thi trên cùng một luồng theo cách đồng bộ hóa, (ngoại trừ trong các trường hợp hiếm hoi khi các quy trình được thiết kế phối hợp với nhau và HĐH chia sẻ các luồng giữa các quy trình.)
Charles Bretana

50

Một câu hỏi nhiều thông tin hơn là điều gì làm cho mã không phải là luồng an toàn - và câu trả lời là có bốn điều kiện phải đúng ... Hãy tưởng tượng đoạn mã sau (và đó là bản dịch ngôn ngữ máy)

totalRequests = totalRequests + 1
MOV EAX, [totalRequests]   // load memory for tot Requests into register
INC EAX                    // update register
MOV [totalRequests], EAX   // store updated value back to memory
  1. Điều kiện đầu tiên là có các vị trí bộ nhớ có thể truy cập từ nhiều hơn một luồng. Thông thường, các vị trí này là các biến toàn cục / tĩnh hoặc có thể truy cập bộ nhớ heap từ các biến toàn cục / tĩnh. Mỗi luồng có khung ngăn xếp riêng của nó cho các biến cục bộ trong phạm vi hàm / phương thức, do đó, các biến phương thức / hàm phương thức cục bộ này, otoh, (nằm trên ngăn xếp) chỉ có thể truy cập được từ một luồng sở hữu ngăn xếp đó.
  2. Điều kiện thứ hai là có một thuộc tính (thường được gọi là bất biến ), được liên kết với các vị trí bộ nhớ dùng chung này, phải đúng hoặc hợp lệ để chương trình hoạt động chính xác. Trong ví dụ trên, thuộc tính là tổng TotalRequests phải thể hiện chính xác tổng số lần bất kỳ luồng nào đã thực hiện bất kỳ phần nào của câu lệnh gia tăng . Thông thường, thuộc tính bất biến này cần phải giữ đúng (trong trường hợp này, TotalRequests phải giữ số đếm chính xác) trước khi cập nhật xảy ra để cập nhật chính xác.
  3. Điều kiện thứ ba là thuộc tính bất biến KHÔNG giữ trong một phần của bản cập nhật thực tế. (Nó tạm thời không hợp lệ hoặc sai trong một số phần của quá trình xử lý). Trong trường hợp cụ thể này, kể từ thời điểm TotalRequests được tìm nạp cho đến khi giá trị cập nhật được lưu trữ, TotalRequests không thỏa mãn bất biến.
  4. Điều kiện thứ tư và cuối cùng phải xảy ra cho một cuộc đua xảy ra (và do đó mã KHÔNG phải là "an toàn luồng") là một luồng khác phải có thể truy cập vào bộ nhớ dùng chung trong khi bất biến bị phá vỡ, do đó gây ra sự không nhất quán hoặc hành vi không đúng.

6
Điều này chỉ bao gồm những gì được gọi là cuộc đua dữ liệu , và tất nhiên là quan trọng. Tuy nhiên, có nhiều cách khác làm thế nào mã không thể an toàn cho chuỗi - ví dụ như khóa xấu có thể dẫn đến bế tắc. Ngay cả một cái gì đó đơn giản như gọi System.exit () ở đâu đó trong luồng java làm cho mã đó không an toàn.
Ingo

2
Tôi đoán ở một mức độ nào đó đây là ngữ nghĩa, nhưng tôi sẽ lập luận rằng mã khóa xấu có thể gây ra bế tắc không làm cho mã không an toàn. Đầu tiên, không cần phải khóa mã ở vị trí đầu tiên trừ khi điều kiện cuộc đua, như được mô tả ở trên, là có thể. Sau đó, nếu bạn viết mã khóa theo cách gây ra bế tắc, điều đó không an toàn cho chuỗi, thì đó chỉ là mã xấu.
Charles Bretana

1
Nhưng lưu ý rằng sự bế tắc sẽ không xảy ra khi chạy một luồng đơn, vì vậy đối với hầu hết chúng ta, điều này chắc chắn sẽ thuộc về ý nghĩa trực quan của (không) "an toàn luồng".
Jon Coombs

Chà, bế tắc không thể xảy ra trừ khi bạn đang chạy đa luồng, nhưng điều đó giống như nói rằng các sự cố mạng không thể xảy ra nếu bạn đang chạy trên một máy. Các vấn đề khác cũng có thể xảy ra đơn luồng, nếu lập trình viên viết mã để nó thoát ra khỏi các dòng mã quan trọng trước khi hoàn thành cập nhật và sửa đổi biến trong một số chương trình con khác.
Charles Bretana

34

Tôi thích định nghĩa từ Thực hành đồng thời Java của Brian Goetz vì tính toàn diện của nó

"Một lớp là an toàn luồng nếu nó hoạt động chính xác khi được truy cập từ nhiều luồng, bất kể việc lập lịch trình hoặc xen kẽ việc thực thi các luồng đó bởi môi trường thời gian chạy và không có sự đồng bộ hóa hoặc phối hợp khác trên một phần của mã gọi. "


Định nghĩa này không đầy đủ và không cụ thể, và chắc chắn không toàn diện. Bao nhiêu lần nó phải chạy an toàn, Chỉ một lần? mười lần? mỗi lần? 80% thời gian? và nó không chỉ định những gì làm cho nó "Không an toàn". Nếu nó không chạy an toàn, nhưng thất bại là do lỗi chia cho 0, điều đó có làm cho nó bị lỗi không - "Không an toàn"?
Charles Bretana

Hãy dân sự hơn vào lần tới và có lẽ chúng ta có thể thảo luận. Đây không phải là Reddit và tôi không có tâm trạng nói chuyện với những người thô lỗ.
Bửu Nguyễn

Giải thích ý kiến ​​của bạn về định nghĩa của người khác là xúc phạm chính mình đang nói. Bạn cần đọc và hiểu chất trước khi phản ứng cảm xúc. Không có gì lạ thường về nhận xét của tôi. Tôi đã đưa ra một quan điểm về ý nghĩa của định nghĩa. Xin lỗi nếu các ví dụ tôi sử dụng để minh họa điểm khiến bạn không thoải mái.
Charles Bretana

28

Như những người khác đã chỉ ra, an toàn luồng có nghĩa là một đoạn mã sẽ hoạt động mà không có lỗi nếu nó được sử dụng bởi nhiều hơn một luồng cùng một lúc.

Điều đáng lưu ý là điều này đôi khi phải trả giá, thời gian của máy tính và mã hóa phức tạp hơn, vì vậy nó không phải lúc nào cũng được mong muốn. Nếu một lớp có thể được sử dụng một cách an toàn trên chỉ một luồng, có thể tốt hơn để làm như vậy.

Ví dụ, Java có hai lớp gần như tương đương StringBufferStringBuilder. Sự khác biệt là StringBufferan toàn luồng, do đó, một thể hiện của một StringBufferluồng có thể được sử dụng bởi nhiều luồng cùng một lúc. StringBuilderkhông an toàn cho luồng và được thiết kế như một sự thay thế hiệu năng cao hơn cho những trường hợp đó (đại đa số) khi Chuỗi được xây dựng chỉ bởi một luồng.


22

Thread-safe-code hoạt động như được chỉ định, ngay cả khi được nhập đồng thời bởi các luồng khác nhau. Điều này thường có nghĩa là, các cấu trúc dữ liệu nội bộ hoặc các hoạt động nên chạy không bị gián đoạn sẽ được bảo vệ chống lại các sửa đổi khác nhau cùng một lúc.


21

Một cách dễ dàng hơn để hiểu nó, đó là những gì làm cho mã không an toàn cho chuỗi. Có hai vấn đề chính sẽ làm cho một ứng dụng luồng có hành vi không mong muốn.

  • Truy cập biến chia sẻ mà không khóa
    Biến này có thể được sửa đổi bởi một luồng khác trong khi thực hiện chức năng. Bạn muốn ngăn chặn nó bằng một cơ chế khóa để chắc chắn về hành vi của chức năng của bạn. Nguyên tắc chung là giữ khóa trong thời gian ngắn nhất có thể.

  • Bế tắc gây ra bởi sự phụ thuộc lẫn nhau vào biến chia sẻ
    Nếu bạn có hai biến chung A và B. Trong một chức năng, bạn khóa A trước sau đó bạn khóa B. Trong một chức năng khác, bạn bắt đầu khóa B và sau một thời gian, bạn khóa A. Điều này là một bế tắc tiềm năng trong đó chức năng đầu tiên sẽ đợi B được mở khóa khi chức năng thứ hai sẽ chờ A được mở khóa. Vấn đề này có thể sẽ không xảy ra trong môi trường phát triển của bạn và chỉ thỉnh thoảng. Để tránh nó, tất cả các khóa phải luôn theo cùng một thứ tự.


9

Có và không.

An toàn luồng là nhiều hơn một chút so với việc đảm bảo dữ liệu chia sẻ của bạn chỉ được truy cập bởi một luồng mỗi lần. Bạn phải đảm bảo truy cập tuần tự vào dữ liệu được chia sẻ, đồng thời tránh các điều kiện chủng tộc , bế tắc , sinh động và bỏ đói tài nguyên .

Kết quả không thể đoán trước khi nhiều luồng đang chạy không phải là điều kiện bắt buộc của mã an toàn luồng, nhưng nó thường là sản phẩm phụ. Ví dụ: bạn có thể có một sơ đồ nhà sản xuất-người tiêu dùng được thiết lập với hàng đợi dùng chung, một luồng sản xuất và một vài luồng tiêu dùng và luồng dữ liệu có thể dự đoán được một cách hoàn hảo. Nếu bạn bắt đầu giới thiệu nhiều người tiêu dùng hơn, bạn sẽ thấy nhiều kết quả tìm kiếm ngẫu nhiên hơn.


9

Về bản chất, nhiều thứ có thể sai trong môi trường đa luồng (hướng dẫn sắp xếp lại, các đối tượng được xây dựng một phần, cùng một biến có các giá trị khác nhau trong các luồng khác nhau do lưu vào bộ đệm ở cấp CPU, v.v.).

Tôi thích định nghĩa được đưa ra bởi Java Concurrency in Practice :

[Phần mã] an toàn cho luồng nếu nó hoạt động chính xác khi được truy cập từ nhiều luồng, bất kể lập lịch hoặc xen kẽ việc thực thi các luồng đó bởi môi trường thời gian chạy và không có đồng bộ hóa bổ sung hoặc phối hợp khác trên một phần của mã gọi.

Bởi chính xác, họ có nghĩa là chương trình hành xử tuân thủ các thông số kỹ thuật của nó.

Ví dụ

Hãy tưởng tượng rằng bạn thực hiện một truy cập. Bạn có thể nói rằng nó hoạt động chính xác nếu:

  • counter.next() không bao giờ trả về một giá trị đã được trả về trước đó (chúng tôi giả sử không có tràn vv để đơn giản)
  • tất cả các giá trị từ 0 đến giá trị hiện tại đã được trả về ở một số giai đoạn (không bỏ qua giá trị nào)

Một bộ đếm an toàn của luồng sẽ hoạt động theo các quy tắc đó bất kể có bao nhiêu luồng truy cập đồng thời (thường không phải là trường hợp thực hiện ngây thơ).

Lưu ý: bài đăng chéo trên Lập trình viên


8

Đơn giản - mã sẽ chạy tốt nếu nhiều luồng đang thực thi mã này cùng một lúc.


5

Đừng nhầm lẫn sự an toàn của chủ đề với tính xác định. Mã an toàn chủ đề cũng có thể không xác định. Với những khó khăn trong việc gỡ lỗi các vấn đề với mã luồng, đây có lẽ là trường hợp bình thường. :-)

An toàn luồng chỉ đơn giản là đảm bảo rằng khi một luồng đang sửa đổi hoặc đọc dữ liệu được chia sẻ, không có luồng nào khác có thể truy cập nó theo cách thay đổi dữ liệu. Nếu mã của bạn phụ thuộc vào một thứ tự nhất định để thực thi cho chính xác, thì bạn cần các cơ chế đồng bộ hóa khác ngoài các cơ chế được yêu cầu về an toàn luồng để đảm bảo điều này.


5

Tôi muốn thêm một số thông tin trên đầu câu trả lời tốt khác.

An toàn luồng ngụ ý nhiều luồng có thể ghi / đọc dữ liệu trong cùng một đối tượng mà không có lỗi không nhất quán bộ nhớ. Trong chương trình đa luồng cao, chương trình an toàn luồng không gây ra tác dụng phụ cho dữ liệu chia sẻ .

Hãy xem câu hỏi SE này để biết thêm chi tiết:

Chủ đề an toàn có nghĩa là gì?

Chương trình an toàn chủ đề đảm bảo tính nhất quán bộ nhớ .

Từ trang tài liệu oracle về API đồng thời nâng cao:

Thuộc tính nhất quán bộ nhớ:

Chương 17 của Đặc tả ngôn ngữ Java ™ xác định mối quan hệ xảy ra trước khi hoạt động của bộ nhớ như đọc và ghi các biến được chia sẻ. Các kết quả của một lần ghi bởi một luồng được đảm bảo hiển thị cho một lần đọc bởi một luồng khác chỉ khi thao tác ghi xảy ra - trước thao tác đọc .

Các cấu trúc synchronizedvolatile, cũng như Thread.start()Thread.join()các phương thức, có thể hình thành xảy ra - trước các mối quan hệ.

Các phương thức của tất cả các lớp trong java.util.concurrentvà các gói con của nó mở rộng các đảm bảo này đến đồng bộ hóa ở mức cao hơn. Đặc biệt:

  1. Các hành động trong một luồng trước khi đặt một đối tượng vào bất kỳ bộ sưu tập đồng thời nào xảy ra - trước các hành động tiếp theo sau khi truy cập hoặc xóa phần tử đó khỏi bộ sưu tập trong một luồng khác.
  2. Các hành động trong một luồng trước khi đệ trình một Runnablesự kiện Executorxảy ra - trước khi việc thực thi của nó bắt đầu. Tương tự cho các Callables gửi đến một ExecutorService.
  3. Các hành động được thực hiện bởi tính toán không đồng bộ được biểu thị bằng một Futurehành động xảy ra trước khi truy xuất kết quả thông qua Future.get()trong một luồng khác.
  4. Các hành động trước khi "giải phóng" các phương thức đồng bộ hóa, chẳng hạn như Lock.unlock, Semaphore.release, and CountDownLatch.countDowncác hành động xảy ra trước các phương thức "thu nhận" thành công, chẳng hạn như Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.awaittrên cùng một đối tượng đồng bộ hóa trong một luồng khác.
  5. Đối với mỗi cặp luồng trao đổi thành công các đối tượng thông qua một Exchanger, các hành động trước exchange()trong mỗi luồng xảy ra - trước các luồng tiếp theo với trao đổi tương ứng () trong luồng khác.
  6. Các hành động trước khi gọi CyclicBarrier.awaitPhaser.awaitAdvance(cũng như các biến thể của nó) xảy ra trước các hành động được thực hiện bởi hành động rào cản và các hành động được thực hiện bởi hành động rào cản xảy ra - trước các hành động sau khi trả lại thành công từ sự chờ đợi tương ứng trong các luồng khác.

4

Để hoàn thành các câu trả lời khác:

Đồng bộ hóa chỉ là một vấn đề đáng lo ngại khi mã trong phương thức của bạn thực hiện một trong hai điều sau:

  1. làm việc với một số tài nguyên bên ngoài không phải là chủ đề an toàn.
  2. Đọc hoặc thay đổi một đối tượng liên tục hoặc trường lớp

Điều này có nghĩa là các biến được định nghĩa trong phương thức của bạn luôn là các luồng an toàn. Mỗi lệnh gọi đến một phương thức có phiên bản riêng của các biến này. Nếu phương thức được gọi bởi một luồng khác hoặc bởi cùng một luồng hoặc ngay cả khi phương thức đó tự gọi (đệ quy), các giá trị của các biến này không được chia sẻ.

Lập lịch trình chủ đề không được đảm bảo là vòng tròn . Một tác vụ hoàn toàn có thể hog CPU với chi phí của các luồng có cùng mức ưu tiên. Bạn có thể sử dụng Thread.yield () để có lương tâm. Bạn có thể sử dụng (trong java) Thread.setP Warriority (Thread.NORM_PRIORITY-1) để hạ mức độ ưu tiên của luồng

Cộng với sự cẩn thận của:

  • chi phí thời gian chạy lớn (đã được đề cập bởi những người khác) trên các ứng dụng lặp qua các cấu trúc "an toàn luồng" này.
  • Thread.s ngủ (5000) được cho là ngủ trong 5 giây. Tuy nhiên, nếu ai đó thay đổi thời gian hệ thống, bạn có thể ngủ rất lâu hoặc không có thời gian. HĐH ghi lại thời gian thức dậy ở dạng tuyệt đối, không tương đối.

2

Vâng và vâng. Nó ngụ ý rằng dữ liệu không được sửa đổi bởi nhiều hơn một luồng cùng một lúc. Tuy nhiên, chương trình của bạn có thể hoạt động như mong đợi và có vẻ an toàn cho chủ đề, ngay cả khi về cơ bản là không.

Lưu ý rằng kết quả không dự đoán được là kết quả của 'điều kiện chủng tộc' có thể dẫn đến dữ liệu bị sửa đổi theo thứ tự khác với dự kiến.


2

Hãy trả lời điều này bằng ví dụ:

class NonThreadSafe {

    private int counter = 0;

    public boolean countTo10() {
        count = count + 1;
        return (count == 10);
    }

Các countTo10phương pháp bổ sung thêm một đến quầy thu ngân và sau đó trả về true nếu số lượng đã đạt 10 Nó chỉ nên trở lại một khi sự thật.

Điều này sẽ hoạt động miễn là chỉ có một luồng đang chạy mã. Nếu hai luồng chạy mã cùng một lúc các vấn đề khác nhau có thể xảy ra.

Ví dụ: nếu số bắt đầu là 9, một luồng có thể thêm 1 để đếm (tạo 10) nhưng sau đó một luồng thứ hai có thể nhập phương thức và thêm 1 lần nữa (tạo 11) trước khi luồng đầu tiên có cơ hội thực hiện so sánh với 10 Sau đó, cả hai luồng thực hiện so sánh và thấy rằng số đó là 11 và không trả về đúng.

Vì vậy, mã này không phải là chủ đề an toàn.

Về bản chất, tất cả các vấn đề đa luồng được gây ra bởi một số biến thể của loại vấn đề này.

Giải pháp là đảm bảo rằng việc bổ sung và so sánh không thể tách rời (ví dụ bằng cách bao quanh hai câu lệnh bằng một loại mã đồng bộ hóa nào đó) hoặc bằng cách nghĩ ra một giải pháp không yêu cầu hai thao tác. Mã như vậy sẽ được an toàn chủ đề.


1

Ít nhất là trong C ++, tôi nghĩ về chủ đề an toàn như một chút sai lầm ở chỗ nó để lại rất nhiều tên. Để an toàn cho chuỗi, mã thường phải chủ động về nó. Nó thường không phải là một chất lượng thụ động.

Để một lớp được an toàn, nó phải có các tính năng "phụ" để thêm chi phí. Các tính năng này là một phần của việc triển khai lớp và nói chung, ẩn khỏi giao diện. Đó là, các luồng khác nhau có thể truy cập bất kỳ thành viên nào của lớp mà không phải lo lắng về việc xung đột với truy cập đồng thời bởi một luồng khác VÀ có thể làm như vậy một cách rất lười biếng, sử dụng kiểu mã hóa thông thường của con người thông thường, mà không phải làm tất cả những thứ đồng bộ hóa điên rồ đó đã được đưa vào ruột của mã được gọi.

Và đây là lý do tại sao một số người thích sử dụng thuật ngữ được đồng bộ hóa nội bộ .

Bộ thuật ngữ

Có ba bộ thuật ngữ chính cho những ý tưởng này mà tôi đã gặp. Đầu tiên và trong lịch sử phổ biến hơn (nhưng tệ hơn) là:

  1. chủ đề an toàn
  2. không an toàn

Thứ hai (và tốt hơn) là:

  1. bằng chứng
  2. tương thích chủ đề
  3. chủ đề thù địch

Thứ ba là:

  1. đồng bộ nội bộ
  2. đồng bộ hóa bên ngoài
  3. không đồng bộ

Tương tự

chủ đề an toàn ~ bằng chứng chủ đề ~ đồng bộ nội bộ

Một ví dụ về hệ thống được đồng bộ hóa nội bộ (còn gọi là an toàn luồng hoặc chứng minh luồng ) là một nhà hàng nơi chủ nhà chào đón bạn ở cửa và không cho phép bạn xếp hàng. Chủ nhà là một phần trong cơ chế của nhà hàng để giao dịch với nhiều khách hàng và có thể sử dụng một số thủ thuật khá phức tạp để tối ưu hóa chỗ ngồi của khách hàng đang chờ, như tính đến quy mô của bữa tiệc của họ, hoặc họ trông như thế nào hoặc thậm chí nhận đặt phòng qua điện thoại. Nhà hàng được đồng bộ hóa nội bộ vì tất cả những thứ này là một phần của giao diện để tương tác với nó.

không an toàn luồng (nhưng đẹp) ~ tương thích luồng ~ được đồng bộ hóa bên ngoài ~ luồng tự do

Giả sử bạn đến ngân hàng. Có một dòng, tức là ganh đua cho các giao dịch viên ngân hàng. Bởi vì bạn không phải là một kẻ man rợ, bạn nhận ra rằng điều tốt nhất để làm giữa lúc tranh giành tài nguyên là xếp hàng như một sinh vật văn minh. Không ai về mặt kỹ thuật làm cho bạn làm điều này. Chúng tôi hy vọng bạn có chương trình xã hội cần thiết để tự làm điều đó. Theo nghĩa này, tiền sảnh ngân hàng được đồng bộ hóa bên ngoài. Chúng ta có nên nói rằng nó không an toàn? đó là những gì ngụ ý là nếu bạn đi với thread-safe , thread-an toàn bộ thuật ngữ lưỡng cực. Nó không phải là một bộ điều khoản rất tốt. Thuật ngữ tốt hơn được đồng bộ hóa bên ngoài,Sảnh ngân hàng không bị nhiều khách hàng truy cập, nhưng nó cũng không thực hiện công việc đồng bộ hóa chúng. Các khách hàng tự làm điều đó.

Điều này cũng được gọi là "miễn phí luồng", trong đó "miễn phí" giống như "không có chấy" - hoặc trong trường hợp này là khóa. Vâng, chính xác hơn, đồng bộ nguyên thủy. Điều đó không có nghĩa là mã có thể chạy trên nhiều luồng mà không cần các nguyên hàm đó. Điều đó chỉ có nghĩa là nó không đi kèm với chúng đã được cài đặt và tùy thuộc vào bạn, người sử dụng mã, để tự cài đặt chúng theo cách bạn thấy phù hợp. Việc cài đặt các nguyên hàm đồng bộ hóa của riêng bạn có thể khó khăn và đòi hỏi phải suy nghĩ kỹ về mã, nhưng cũng có thể dẫn đến chương trình nhanh nhất có thể bằng cách cho phép bạn tùy chỉnh cách chương trình thực thi trên các CPU siêu phân luồng ngày nay.

không an toàn chủ đề (và xấu) ~ chủ đề thù địch ~ không đồng bộ

Một ví dụ tương tự hàng ngày của một hệ thống thù địch chủ đề là một số người bị giật với một chiếc xe thể thao từ chối sử dụng đèn nháy và thay đổi làn đường willy-nilly. Phong cách lái xe của họ là chủ đề thù địch hoặc không đồng bộ vì bạn không có cách nào để phối hợp với họ và điều này có thể dẫn đến sự tranh chấp cho cùng một làn đường, không có độ phân giải, và do đó, một tai nạn khi hai chiếc xe cố gắng chiếm cùng một không gian, mà không có bất kỳ giao thức nào ngăn chặn điều này Mô hình này cũng có thể được coi rộng rãi hơn là chống xã hội, mà tôi thích vì nó ít cụ thể hơn đối với các luồng và vì vậy thường áp dụng cho nhiều lĩnh vực lập trình.

Tại sao chủ đề an toàn et al. là một thuật ngữ xấu được thiết lập

Bộ thuật ngữ đầu tiên và lâu đời nhất không thể phân biệt rõ hơn giữa tính thù địch của luồngkhả năng tương thích của luồng . Khả năng tương thích luồng là thụ động hơn so với cái gọi là an toàn luồng, nhưng điều đó không có nghĩa là mã được gọi là không an toàn khi sử dụng luồng đồng thời. Nó chỉ có nghĩa là nó thụ động về việc đồng bộ hóa sẽ cho phép điều này, đưa nó vào mã gọi, thay vì cung cấp nó như là một phần của việc triển khai nội bộ. Tương thích luồng là cách mã có thể được viết theo mặc định trong hầu hết các trường hợp, nhưng điều này cũng đáng buồn thường được coi là không an toàn của luồng, như thể nó vốn là chống an toàn, đó là một điểm gây nhầm lẫn lớn cho các lập trình viên.

LƯU Ý: Nhiều hướng dẫn sử dụng phần mềm thực sự sử dụng thuật ngữ "an toàn luồng" để chỉ "tương thích luồng", làm tăng thêm sự nhầm lẫn cho những gì đã là một mớ hỗn độn! Tôi tránh thuật ngữ "an toàn luồng" và "không an toàn luồng" bằng mọi giá vì lý do này, vì một số nguồn sẽ gọi một cái gì đó là "an toàn luồng" trong khi những người khác gọi nó là "không an toàn luồng" vì họ không đồng ý về việc bạn phải đáp ứng một số tiêu chuẩn bổ sung về an toàn (nguyên thủy đồng bộ hóa) hay chỉ KHÔNG được thù địch để được coi là "an toàn". Vì vậy, tránh các điều khoản đó và sử dụng các thuật ngữ thông minh hơn thay vào đó, để tránh các thông tin sai lệch nguy hiểm với các kỹ sư khác.

Nhắc nhở về mục tiêu của chúng tôi

Về cơ bản, mục tiêu của chúng tôi là lật đổ sự hỗn loạn.

Chúng tôi làm điều đó bằng cách tạo ra các hệ thống xác định mà chúng tôi có thể dựa vào. Sự quyết đoán là tốn kém, chủ yếu là do chi phí cơ hội của việc mất song song, đường ống và sắp xếp lại. Chúng tôi cố gắng giảm thiểu số lượng quyết định mà chúng tôi cần để giữ chi phí của mình ở mức thấp, đồng thời tránh đưa ra các quyết định sẽ làm xói mòn thêm những gì mà chúng tôi có thể chi trả.

Đồng bộ hóa các chủ đề là về việc tăng thứ tự và giảm sự hỗn loạn. Các cấp độ mà bạn làm điều này tương ứng với các điều khoản được đề cập ở trên. Mức cao nhất có nghĩa là một hệ thống hành xử theo cách hoàn toàn có thể dự đoán được mọi lúc. Cấp độ thứ hai có nghĩa là hệ thống hoạt động đủ tốt để gọi mã có thể phát hiện một cách đáng tin cậy. Ví dụ: đánh thức giả trong một biến điều kiện hoặc không khóa được mutex vì nó chưa sẵn sàng. Cấp độ thứ ba có nghĩa là hệ thống không hoạt động đủ tốt để chơi với bất kỳ ai khác và chỉ có thể EVER được chạy một luồng mà không phát sinh sự hỗn loạn.


1

Thay vì nghĩ hoặc các lớp là luồng an toàn hay không, tôi nghĩ sẽ hữu ích hơn khi nghĩ các hành động là an toàn luồng. Hai hành động là luồng an toàn nếu chúng sẽ hoạt động như được chỉ định khi chạy từ bối cảnh luồng tùy ý. Trong nhiều trường hợp, các lớp sẽ hỗ trợ một số kết hợp các hành động theo kiểu an toàn luồng và các trường hợp khác thì không.

Ví dụ, nhiều bộ sưu tập như danh sách mảng và bộ băm sẽ đảm bảo rằng nếu ban đầu chúng chỉ được truy cập bằng một luồng và chúng không bao giờ được sửa đổi sau khi tham chiếu trở nên hiển thị với bất kỳ luồng nào khác, chúng có thể được đọc theo cách tùy ý của chủ đề mà không can thiệp.

Thú vị hơn, một số bộ sưu tập băm như bộ không gốc chung trong .NET, có thể đảm bảo rằng miễn là không có mục nào bị xóa và chỉ cung cấp một luồng duy nhất ghi cho chúng, bất kỳ luồng nào cố gắng đọc bộ sưu tập sẽ hoạt động như thể truy cập vào một bộ sưu tập trong đó các cập nhật có thể bị trì hoãn và xảy ra theo thứ tự tùy ý, nhưng nếu không thì sẽ hoạt động bình thường. Nếu luồng số 1 thêm X và sau đó là Y, và luồng số 2 tìm và thấy Y và sau đó là X, thì luồng số 2 có thể thấy rằng Y tồn tại nhưng X thì không; hành vi đó có "an toàn cho luồng" hay không sẽ phụ thuộc vào việc luồng số 2 có được chuẩn bị để đối phó với khả năng đó hay không.

Lưu ý cuối cùng, một số lớp - đặc biệt là chặn các thư viện truyền thông - có thể có phương thức "đóng" hoặc "Loại bỏ" an toàn cho luồng đối với tất cả các phương thức khác, nhưng không có phương thức nào khác an toàn cho luồng đối với lẫn nhau. Nếu một luồng thực hiện yêu cầu đọc chặn và người dùng chương trình nhấp vào "hủy", sẽ không có cách nào để yêu cầu đóng được đưa ra bởi luồng đang cố thực hiện đọc. Tuy nhiên, yêu cầu đóng / loại bỏ có thể đặt cờ không đồng bộ sẽ khiến yêu cầu đọc bị hủy càng sớm càng tốt. Khi đóng được thực hiện trên bất kỳ luồng nào, đối tượng sẽ trở nên vô dụng và mọi nỗ lực trong các hành động trong tương lai sẽ thất bại ngay lập tức,


0

Nói một cách đơn giản nhất: P Nếu an toàn để thực thi nhiều luồng trên một khối mã thì đó là luồng an toàn *

* điều kiện áp dụng

Các điều kiện được đề cập bởi các câu trả lời khác như 1. Kết quả sẽ giống nhau nếu bạn thực thi một luồng hoặc nhiều luồng trên nó, v.v.

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.