Câu trả lời:
Không, chúng không giống nhau. Chúng không phải là một tập hợp con của nhau. Chúng cũng không phải là điều kiện cần, cũng không phải là điều kiện đủ cho nhau.
Định nghĩa về một cuộc đua dữ liệu khá rõ ràng và do đó, việc khám phá nó có thể được tự động hóa. Một cuộc chạy đua dữ liệu xảy ra khi 2 lệnh từ các luồng khác nhau truy cập vào cùng một vị trí bộ nhớ, ít nhất một trong các truy cập này là ghi và không có sự đồng bộ hóa bắt buộc bất kỳ thứ tự cụ thể nào giữa các truy cập này.
Điều kiện chủng tộc là một lỗi ngữ nghĩa. Đó là một sai sót xảy ra trong thời gian hoặc thứ tự của các sự kiện dẫn đến hành vi chương trình sai. Nhiều tình trạng cuộc đua có thể do cuộc đua dữ liệu gây ra, nhưng điều này không cần thiết.
Hãy xem xét ví dụ đơn giản sau trong đó x là một biến được chia sẻ:
Thread 1 Thread 2
lock(l) lock(l)
x=1 x=2
unlock(l) unlock(l)
Trong ví dụ này, các lần ghi vào x từ luồng 1 và 2 được bảo vệ bằng các khóa, do đó chúng luôn xảy ra theo một số thứ tự được thực thi bởi thứ tự mà các khóa có được trong thời gian chạy. Đó là, tính nguyên tử được viết 'không thể bị phá vỡ; luôn luôn có một điều gì đó xảy ra trước khi mối quan hệ giữa hai viết trong bất kỳ quá trình thực thi nào. Chúng ta chỉ không thể biết viết nào xảy ra trước tiên nghiệm khác.
Không có thứ tự cố định giữa các lần ghi, vì khóa không thể cung cấp điều này. Nếu tính đúng đắn của chương trình bị xâm phạm, giả sử khi ghi vào x bởi luồng 2 được theo sau bởi ghi vào x trong luồng 1, chúng ta nói rằng có một điều kiện chạy đua, mặc dù về mặt kỹ thuật không có chủng tộc dữ liệu.
Nó hữu ích hơn nhiều để phát hiện điều kiện cuộc đua so với cuộc đua dữ liệu; tuy nhiên điều này cũng rất khó đạt được.
Việc xây dựng ví dụ ngược lại cũng rất tầm thường. Bài đăng trên blog này cũng giải thích sự khác biệt rất tốt, với một ví dụ giao dịch ngân hàng đơn giản.
Theo Wikipedia, thuật ngữ "điều kiện chủng tộc" đã được sử dụng kể từ ngày của những cổng logic điện tử đầu tiên. Trong ngữ cảnh của Java, một điều kiện chủng tộc có thể liên quan đến bất kỳ tài nguyên nào, chẳng hạn như tệp, kết nối mạng, luồng từ nhóm luồng, v.v.
Thuật ngữ "cuộc đua dữ liệu" được dành riêng cho ý nghĩa cụ thể của nó được định nghĩa bởi JLS .
Trường hợp thú vị nhất là một điều kiện chủng tộc rất giống với chủng tộc dữ liệu, nhưng vẫn không phải là một, như trong ví dụ đơn giản này:
class Race {
static volatile int i;
static int uniqueInt() { return i++; }
}
Vì i
là không ổn định, không có cuộc chạy đua dữ liệu; tuy nhiên, từ quan điểm độ đúng của chương trình có một điều kiện chạy đua do tính phi nguyên tử của hai hoạt động: đọc i
, ghi i+1
. Nhiều chủ đề có thể nhận cùng một giá trị từ uniqueInt
.
data race
nghĩa thực sự trong JLS không?
Không, chúng khác nhau và cả hai đều không phải là tập hợp con của một hoặc ngược lại.
Thuật ngữ điều kiện chủng tộc thường bị nhầm lẫn với thuật ngữ chủng tộc dữ liệu có liên quan, phát sinh khi đồng bộ hóa không được sử dụng để điều phối tất cả quyền truy cập vào trường không chính thức được chia sẻ. Bạn có nguy cơ chạy đua dữ liệu bất cứ khi nào một luồng ghi một biến mà có thể tiếp theo sẽ được đọc bởi một luồng khác hoặc đọc một biến có thể đã được ghi lần cuối bởi một luồng khác nếu cả hai luồng không sử dụng đồng bộ hóa; mã với các chủng tộc dữ liệu không có ngữ nghĩa xác định hữu ích theo Mô hình Bộ nhớ Java. Không phải tất cả các điều kiện cuộc đua đều là cuộc đua dữ liệu và không phải tất cả cuộc đua dữ liệu đều là điều kiện cuộc đua, nhưng cả hai đều có thể khiến các chương trình đồng thời bị lỗi theo những cách không thể đoán trước.
Trích từ cuốn sách xuất sắc - Java Concurrency in Practice của Joshua Bloch & Co.
TL; DR: Sự phân biệt giữa chủng tộc dữ liệu và điều kiện chủng tộc phụ thuộc vào bản chất của việc xây dựng vấn đề và nơi vẽ ranh giới giữa hành vi không xác định và hành vi được xác định rõ nhưng không xác định. Sự khác biệt hiện tại là thông thường và phản ánh tốt nhất giao diện giữa kiến trúc bộ xử lý và ngôn ngữ lập trình.
1. Ngữ nghĩa
Cuộc đua dữ liệu đề cập cụ thể đến các "truy cập bộ nhớ" (hoặc các hành động hoặc hoạt động) xung đột không được đồng bộ hóa vào cùng một vị trí bộ nhớ. Nếu không có xung đột trong truy cập bộ nhớ, trong khi vẫn có hành vi không xác định được gây ra bởi thứ tự hoạt động, thì đó là điều kiện chủng tộc.
Lưu ý "truy cập bộ nhớ" ở đây có nghĩa cụ thể. Chúng đề cập đến hành động tải hoặc lưu trữ bộ nhớ "thuần túy" mà không áp dụng bất kỳ ngữ nghĩa bổ sung nào. Ví dụ, một kho lưu trữ bộ nhớ từ một luồng không (nhất thiết) biết mất bao lâu để dữ liệu được ghi vào bộ nhớ và cuối cùng được truyền sang một luồng khác. Ví dụ khác, một bộ nhớ lưu trữ đến một vị trí trước khi lưu trữ đến một vị trí khác theo cùng một chuỗi không (nhất thiết) đảm bảo dữ liệu đầu tiên được ghi trong bộ nhớ trước dữ liệu thứ hai. Do đó, thứ tự của các truy cập bộ nhớ thuần túy đó không (nhất thiết) có thể được "lý luận" , và bất cứ điều gì có thể xảy ra, trừ khi được xác định rõ ràng.
Khi các "truy cập bộ nhớ" được xác định rõ ràng về thứ tự thông qua đồng bộ hóa, ngữ nghĩa bổ sung có thể đảm bảo rằng, ngay cả khi thời gian của các truy cập bộ nhớ là không xác định, thứ tự của chúng có thể được "lý giải" thông qua đồng bộ hóa. Lưu ý, mặc dù thứ tự giữa các truy cập bộ nhớ có thể được giải thích, chúng không nhất thiết phải xác định, do đó điều kiện chủng tộc.
2. Tại sao sự khác biệt?
Nhưng nếu thứ tự vẫn không xác định trong điều kiện chủng tộc, tại sao phải phân biệt nó với chủng tộc dữ liệu? Lý do là trong thực tế hơn là lý thuyết. Đó là bởi vì sự khác biệt tồn tại trong giao diện giữa ngôn ngữ lập trình và kiến trúc bộ xử lý.
Lệnh tải / lưu trữ bộ nhớ trong kiến trúc hiện đại thường được thực hiện dưới dạng truy cập bộ nhớ "thuần túy", do tính chất của đường ống không theo thứ tự, đầu cơ, đa cấp bộ nhớ cache, kết nối cpu-ram, đặc biệt là đa lõi, v.v. Có rất nhiều yếu tố dẫn đến thời gian và thứ tự không xác định. Để thực thi thứ tự cho mọi lệnh bộ nhớ sẽ bị phạt rất lớn, đặc biệt là trong thiết kế bộ xử lý hỗ trợ đa lõi. Vì vậy, ngữ nghĩa thứ tự được cung cấp với các hướng dẫn bổ sung như các hàng rào (hoặc hàng rào) khác nhau.
Chạy đua dữ liệu là tình huống thực thi lệnh của bộ xử lý mà không có hàng rào bổ sung để giúp suy luận thứ tự của các truy cập bộ nhớ xung đột. Kết quả không chỉ là không xác định, mà còn có thể rất kỳ lạ, ví dụ: hai lần ghi vào cùng một vị trí từ bởi các chuỗi khác nhau có thể dẫn đến mỗi lần viết một nửa từ hoặc có thể chỉ hoạt động dựa trên các giá trị được lưu trong bộ nhớ cache cục bộ của chúng. - Đây là những hành vi không xác định, theo quan điểm của lập trình viên. Nhưng chúng (thường) được xác định rõ ràng theo quan điểm của kiến trúc sư bộ xử lý.
Các lập trình viên phải có một cách để lý giải việc thực thi mã của họ. Cuộc đua dữ liệu là thứ mà chúng không thể có ý nghĩa, do đó luôn phải tránh (bình thường). Đó là lý do tại sao các đặc tả ngôn ngữ đủ mức thấp thường xác định chủng tộc dữ liệu là hành vi không xác định, khác với hành vi bộ nhớ được xác định rõ ràng về điều kiện chủng tộc.
3. Các mô hình bộ nhớ ngôn ngữ
Các bộ xử lý khác nhau có thể có hành vi truy cập bộ nhớ khác nhau, tức là, mô hình bộ nhớ bộ xử lý. Thật khó cho các lập trình viên khi nghiên cứu mô hình bộ nhớ của mọi bộ xử lý hiện đại và sau đó phát triển các chương trình có thể thu được lợi ích từ chúng. Nó là mong muốn nếu ngôn ngữ có thể xác định một mô hình bộ nhớ để các chương trình của ngôn ngữ đó luôn hoạt động như mong đợi như mô hình bộ nhớ xác định. Đó là lý do tại sao Java và C ++ có các mô hình bộ nhớ của chúng được định nghĩa. Đó là gánh nặng của các nhà phát triển trình biên dịch / thời gian chạy để đảm bảo các mô hình bộ nhớ ngôn ngữ được thực thi trên các kiến trúc bộ xử lý khác nhau.
Điều đó nói rằng, nếu một ngôn ngữ không muốn bộc lộ hành vi cấp thấp của bộ xử lý (và sẵn sàng hy sinh một số lợi ích hiệu suất nhất định của các kiến trúc hiện đại), họ có thể chọn xác định một mô hình bộ nhớ hoàn toàn ẩn các chi tiết của "thuần túy" truy cập bộ nhớ, nhưng áp dụng ngữ nghĩa sắp xếp cho tất cả các hoạt động bộ nhớ của chúng. Sau đó, các nhà phát triển trình biên dịch / thời gian chạy có thể chọn coi mọi biến bộ nhớ là dễ bay hơi trong tất cả các kiến trúc bộ xử lý. Đối với những ngôn ngữ này (hỗ trợ bộ nhớ chia sẻ trên các luồng), không có chủng tộc dữ liệu, nhưng vẫn có thể là điều kiện chủng tộc, ngay cả với ngôn ngữ có tính nhất quán tuần tự hoàn toàn.
Mặt khác, mô hình bộ nhớ của bộ xử lý có thể chặt chẽ hơn (hoặc ít thoải mái hơn, hoặc ở mức cao hơn), ví dụ, thực hiện nhất quán tuần tự như bộ xử lý ngày đầu đã làm. Sau đó, tất cả các hoạt động của bộ nhớ được sắp xếp theo thứ tự và không có cuộc đua dữ liệu nào tồn tại cho bất kỳ ngôn ngữ nào đang chạy trong bộ xử lý.
4. Kết luận
Quay lại câu hỏi ban đầu, IMHO có thể định nghĩa cuộc đua dữ liệu là một trường hợp đặc biệt của điều kiện chủng tộc, và điều kiện chủng tộc ở một cấp có thể trở thành chủng tộc dữ liệu ở cấp cao hơn. Nó phụ thuộc vào bản chất của việc xây dựng vấn đề và nơi vẽ ranh giới giữa hành vi không xác định và hành vi được xác định rõ nhưng không xác định. Chỉ quy ước hiện tại xác định ranh giới tại giao diện ngôn ngữ-bộ xử lý, không nhất thiết có nghĩa là luôn và phải như vậy; nhưng quy ước hiện tại có lẽ phản ánh tốt nhất giao diện hiện đại (và sự khôn ngoan) giữa kiến trúc sư bộ xử lý và ngôn ngữ lập trình.