Giả sử chúng ta có một hàng đợi không có khóa đơn-người tiêu dùng-thread-đơn-sản xuất, và rằng nhà sản xuất có thể mất nhiều thời gian mà không tạo ra bất kỳ dữ liệu nào. Nó sẽ có lợi để cho phép người tiêu dùng ngủ khi không có gì trong hàng đợi (để tiết kiệm năng lượng và giải phóng CPU cho các quá trình/chủ đề khác). Nếu hàng đợi không khóa, cách đơn giản để giải quyết vấn đề này là phải tạo ra một chuỗi khóa, hãy thực hiện công việc của nó, báo hiệu biến điều kiện và mở khóa, và cho chuỗi đọc để khóa mutex, chờ trên biến điều kiện , đọc, sau đó mở khóa. Nhưng nếu chúng ta đang sử dụng một hàng đợi không khóa, sử dụng một mutex chính xác cùng một cách sẽ loại bỏ hiệu suất chúng ta thu được từ việc sử dụng một hàng đợi không khóa ở nơi đầu tiên.Phương pháp miễn phí cuộc đua nhanh nhất để bỏ phiếu cho hàng đợi không khóa là gì?
Giải pháp ngây thơ là có nhà sản xuất sau mỗi lần chèn vào hàng đợi khóa mutex, báo hiệu biến điều kiện, sau đó mở khóa mutex, giữ công việc thực tế (chèn vào hàng đợi) hoàn toàn bên ngoài khóa và có người tiêu dùng làm tương tự, khóa mutex, chờ đợi trên biến điều kiện, mở khóa nó, kéo tất cả mọi thứ ra khỏi hàng đợi, sau đó lặp lại, giữ việc đọc hàng đợi bên ngoài khóa. Có một điều kiện chủng tộc ở đây mặc dù: giữa người đọc kéo ra khỏi hàng đợi và đi ngủ, nhà sản xuất có thể đã chèn một vật phẩm vào hàng đợi. Bây giờ người đọc sẽ đi ngủ, và có thể ở lại vô thời hạn cho đến khi người sản xuất chèn một vật phẩm khác và báo hiệu biến điều kiện một lần nữa. Điều này có nghĩa là đôi khi bạn có thể kết thúc với các mục cụ thể dường như mất một thời gian rất dài để đi qua hàng đợi. Nếu hàng đợi của bạn luôn hoạt động liên tục, điều này có thể không phải là vấn đề, nhưng nếu nó luôn hoạt động, bạn có thể quên hoàn toàn biến điều kiện.
AFAICT giải pháp là dành cho nhà sản xuất để hành xử giống như thể nó đang hoạt động với hàng đợi khóa thông thường. Nó sẽ khóa mutex, chèn vào hàng đợi không khóa, báo hiệu biến điều kiện, mở khóa. Tuy nhiên, người tiêu dùng nên hành xử khác nhau. Khi nó thức dậy, nó sẽ mở khóa mutex ngay lập tức thay vì chờ đợi cho đến khi nó đọc hàng đợi. Sau đó, nó sẽ kéo càng nhiều hàng đợi càng tốt và xử lý nó. Cuối cùng, chỉ khi người tiêu dùng nghĩ đến việc đi ngủ, nó có nên khóa mutex hay không, kiểm tra xem có bất kỳ dữ liệu nào không, sau đó nếu mở khóa và xử lý nó hoặc nếu không thì hãy đợi biến điều kiện. Bằng cách này, mutex được ít thường xuyên hơn so với hàng đợi có khóa, nhưng không có nguy cơ đi ngủ với dữ liệu vẫn còn trên hàng đợi.
Đây có phải là cách tốt nhất để làm điều đó không? Có lựa chọn thay thế nào không?
Note: Bằng 'nhanh nhất' Tôi thực sự có nghĩa là 'nhanh nhất mà không cống hiến một lõi để kiểm tra hàng đợi hơn và hơn,' nhưng điều đó sẽ không phù hợp trong tiêu đề; p
Một thay thế: Đi với các giải pháp ngây thơ, nhưng có người tiêu dùng chờ đợi trên biến điều kiện với một thời gian chờ tương ứng với độ trễ tối đa bạn sẵn sàng chịu đựng cho một mục đi qua hàng đợi. Nếu thời gian chờ mong muốn là khá ngắn mặc dù, nó có thể dưới thời gian chờ đợi tối thiểu cho hệ điều hành của bạn hoặc vẫn tiêu thụ quá nhiều CPU.
Bạn không thể cho nhà sản xuất báo hiệu biến điều kiện mỗi khi nó tạo ra thứ gì đó? Tại sao nó cần một mutex? – Gabe
@Gabe: Hai lý do. Đầu tiên, trong trường hợp này, bởi vì nhà sản xuất có thể tạo ra thứ gì đó và kích hoạt tín hiệu giữa khi người tiêu dùng kết thúc xử lý một mục và khi nó quyết định đợi biến điều kiện. Sau đó, người tiêu dùng sẽ đi vào giấc ngủ và vật phẩm mà nhà sản xuất tạo ra sẽ bị kẹt trên hàng đợi cho đến khi nó phát ra tín hiệu. Thứ hai, vì ít nhất trong API pthreads, bạn không thể sử dụng biến điều kiện mà không có mutexes. Bạn thực sự phải vượt qua một mutex trong chức năng chờ đợi. Tôi không biết liệu tất cả việc triển khai các biến điều kiện có thực sự yêu cầu chúng hay không. –
@Gabe: Một quan niệm sai lầm có thể khiến bạn nghĩ rằng nếu nghĩ rằng nếu tín hiệu được kích hoạt khi không có gì đang chờ trên biến điều kiện, thì lần tiếp theo, thứ gì đó chờ trên biến điều kiện, nó sẽ bị đánh thức ngay lập tức, nhưng đó không phải là không phải vậy. Nếu bạn không chờ đợi trên biến điều kiện khi tín hiệu cháy, như xa như bạn biết nó không bao giờ xảy ra. Trong ý nghĩa này, việc chờ đợi trên một biến điều kiện khác với việc sử dụng poll/select trên một file/socket/pipe. –