2016-01-01 17 views
5

Từ đây: (!) Logic error in my defined Mutex class and the way I use it in producer consumer program - pthreadsTại sao chuyển tham chiếu đến lớp Mutex không phải là thiết kế tốt?

theo cách bạn vượt qua xung quanh tham chiếu đến lớp mutex của bạn được rõ ràng chuốc lấy phiền, nó thách thức bất kỳ loại đóng gói.

Tại sao đây là sự cố? Tôi có nên đã vượt qua giá trị và sau đó viết các nhà xây dựng bản sao?

Những tác hại nào có thể thiếu sự đóng gói trong trường hợp này? Tôi nên gói gọn cái gì?

Ngoài ra, Why is passing references to the Mutex class not a good design?

Đi qua một tham chiếu đến khóa là một ý tưởng tồi - bạn không "sử dụng" khóa, bạn chỉ tiếp thu và sau đó bạn cho nó trở lại. Di chuyển nó xung quanh làm cho nó khó khăn để theo dõi việc bạn sử dụng tài nguyên (quan trọng). Việc chuyển một tham chiếu đến một biến mutex thay vì khóa có thể không quá tệ, nhưng nó vẫn làm cho nó khó khăn hơn để biết những phần nào của chương trình có thể bế tắc, do đó cái gì đó cần tránh.

Vui lòng giải thích bằng ngôn ngữ đơn giản với các ví dụ - tại sao truyền tham chiếu ý tưởng tồi?

+1

Vâng, thông thường bạn sử dụng mutexes một cách cực kỳ cẩn thận. Khi bạn khóa một mutex, bạn muốn khóa có thời gian rất ngắn và được lựa chọn cẩn thận để bạn không gây ra bế tắc. Thông qua một tham chiếu đến khóa là một ý tưởng tồi - bạn không "sử dụng" khóa, bạn chỉ có được và sau đó bạn đưa nó trở lại. Di chuyển nó xung quanh làm cho nó khó khăn để theo dõi việc bạn sử dụng tài nguyên (quan trọng). Việc chuyển một tham chiếu đến một biến mutex thay vì khóa có thể không quá tệ, nhưng nó vẫn làm cho nó khó khăn hơn để biết những phần nào của chương trình có thể bế tắc, do đó cái gì đó cần tránh. Thông thường bạn không bao giờ cần phải làm điều đó. –

+0

Ngoài ra nếu bạn đặc biệt đang chuyển "pthread_mutex &" vào lớp wrapper mutex của bạn, tôi có nghĩa là loại xấu xí ... cho một, bạn có thể nhận được một tham chiếu lơ lửng nếu bạn không cẩn thận, và cho hai, bạn sau đó được gắn để thực hiện pthread. Có lẽ điểm của lớp học của bạn là để che đậy các chi tiết thực hiện, nhưng ở đây bạn đang blatantly rò rỉ chúng! –

+0

@ChrisBeck cảm ơn bạn đã cố gắng giải thích. Tôi yêu cầu bạn giải quyết vấn đề và giải pháp của nó trong "câu trả lời". –

Trả lời

3

Tôi muốn chia nhỏ câu hỏi này thành các câu hỏi riêng biệt: 1. Khi nào nó thích hợp để truyền bất kỳ đối tượng nào bằng cách tham chiếu? 2. Khi nào thích hợp để chia sẻ một mutex?

  1. Cách bạn chuyển đối tượng làm đối số phản ánh cách bạn mong muốn chia sẻ toàn bộ thời gian tồn tại của đối tượng giữa người gọi và callee. Nếu bạn vượt qua tham chiếu, bạn phải giả sử rằng callee sẽ chỉ sử dụng đối tượng trong suốt thời gian của cuộc gọi hoặc, nếu tham chiếu được lưu trữ bởi callee, thời gian của callee ngắn hơn tham chiếu. Nếu giao dịch với các đối tượng được phân bổ động, có thể bạn đang sử dụng các con trỏ thông minh, trong đó có các con trỏ thông minh cho phép bạn giao tiếp rõ ràng hơn ý định của bạn (xem Herb Sutter's treatment của chủ đề này).

  2. Nên tránh chia sẻ một mutex. Điều này đúng cho dù đi qua tham chiếu hay bất kỳ phương tiện nào khác. Bằng cách chia sẻ một mutex, một đối tượng cho phép chính nó bị ảnh hưởng nội bộ bởi một thực thể bên ngoài. Điều đó vi phạm đóng gói cơ bản và là lý do đủ. (Xem bất kỳ văn bản về lập trình hướng đối tượng cho các nhân đức của đóng gói). Một hậu quả thực sự trong việc chia sẻ mutex là khả năng bế tắc.

    Hãy ví dụ này đơn giản:

    • A sở hữu mutex
    • Một cổ phiếu mutex với B
    • B có được khóa trước khi chức năng kêu gọi Một
    • chức năng của một nỗ lực để có được những khóa
    • Deadlock ..

Từ quan điểm thiết kế, tại sao bạn muốn chia sẻ một mutex? Một mutex bảo vệ tài nguyên có thể được truy cập bởi nhiều luồng. Đó là mutex nên được ẩn (đóng gói) trong một lớp điều khiển tài nguyên đó. Một mutex chỉ là một cách mà lớp này có thể bảo vệ tài nguyên; một chi tiết triển khai của nó chỉ là lớp cần phải biết. Thay vào đó, hãy chia sẻ cá thể của lớp kiểm soát tài nguyên và cho phép nó đảm bảo an toàn luồng trong chính nó theo bất kỳ cách nào nó muốn.

3

Tôi nghĩ rằng nó trừu tượng xấu cũng như đóng gói không tốt. a mutex thường được xây dựng mặc định với các hàm tạo bản sao bị xóa, có nhiều đối tượng mutex tham chiếu đến cùng một đối tượng lôgic dễ bị lỗi, tức là nó có thể dẫn đến deadlocks và các điều kiện chủng tộc khác vì lập trình viên hoặc người đọc có thể giả định rằng chúng là các thực thể khác nhau.

Hơn nữa bằng cách chỉ định mutex nội bộ nào bạn đang sử dụng, bạn sẽ hiển thị chi tiết triển khai các chuỗi của bạn, do đó phá vỡ lớp trừu tượng của lớp Mutex. Nếu bạn đang sử dụng pthread_mutex_t thì rất có thể bạn sẽ sử dụng các chuỗi hạt nhân (pthreads).

Việc đóng gói cũng bị hỏng vì Mutex của bạn không phải là một thực thể được đóng gói duy nhất mà nằm rải rác thành một số tham chiếu (có thể đang lơ lửng).

Nếu bạn muốn để đóng gói một pthread_mutex_t vào một lớp học mà bạn sẽ làm điều đó như vậy

class Mutex { 
public: 
    void lock(); 
    void unlock(); 

    // Default and move constructors are good! 
    // You can store a mutex in STL containers with these 
    Mutex(); 
    Mutex(Mutex&&); 
    ~Mutex(); 

    // These can lead to deadlocks! 
    Mutex(const Mutex&) = delete; 
    Mutex& operator= (const Mutex&) = delete; 
    Mutex& operator= (Mutex&&) = delete; 

private: 
    pthread_mutex_t internal_mutex; 
}; 

đối tượng Mutex có nghĩa là để được chia sẻ trong một phạm vi chia sẻ công bố trong các tập tin thực hiện thay vì phải nó tuyên bố tại địa phương và được truyền xung quanh trong các hàm như một tham chiếu. Bạn sẽ lý tưởng chỉ vượt qua trong các đối số cho constructor thread mà bạn cần. Việc truyền tham chiếu đến các đối tượng được khai báo trong một phạm vi ở cùng một mức "" như hàm đang được đề cập (việc thực thi luồng trong trường hợp này) thường dẫn đến các lỗi trong mã. Điều gì sẽ xảy ra nếu phạm vi trong đó mutex được khai báo là không tồn tại nữa? Liệu destructor của mutex có làm mất hiệu lực hoạt động bên trong của mutex không? Điều gì sẽ xảy ra nếu mutex thực hiện theo cách của mình đến một mô-đun khác thông qua việc được truyền xung quanh và mô-đun đó bắt đầu chủ đề của riêng nó và nghĩ rằng mutex sẽ không bao giờ chặn, điều này có thể dẫn đến deadlocks khó chịu. Ngoài ra, một trường hợp bạn muốn sử dụng hàm tạo mutex di chuyển được nói trong một mẫu nhà máy mutex, nếu bạn muốn tạo một mutex mới, bạn sẽ thực hiện một cuộc gọi hàm và hàm đó sẽ trả về một mutex mà bạn sẽ làm sau đó thêm vào mất của bạn mutexes hoặc vượt qua để một sợi đó là yêu cầu nó thông qua một số loại dữ liệu được chia sẻ (danh sách nói trên sẽ là một ý tưởng tốt cho dữ liệu được chia sẻ này).Tuy nhiên, việc tạo mô hình nhà máy mutex như vậy có thể khá phức tạp vì bạn cần khóa quyền truy cập vào danh sách các mutex phổ biến. Nó sẽ là một điều thú vị để thử!

Nếu ý định của tác giả là tránh phạm vi toàn cầu thì hãy khai báo nó một lần trong tệp triển khai dưới dạng đối tượng tĩnh nên đủ trừu tượng.

+0

cảm ơn nhiều. Tôi đã dành thời gian để hiểu câu trả lời của bạn. Điều tôi đã hiểu là nếu chúng ta sử dụng hàm khởi tạo thay vì đi qua tham chiếu, chúng ta sẽ không phải lo lắng về đối tượng cục bộ bên ngoài bị xóa. Điều này có đúng không? Có điều gì khác ngoài điều này mà bạn muốn truyền tải qua câu trả lời của bạn không? –

+0

Đúng, tôi muốn truyền đạt những thứ khác thông qua câu trả lời của mình. Trước hết, các hàm tạo di chuyển có ích với các mutex không phải vì bạn sẽ muốn truyền chúng xung quanh nhưng trong trường hợp bạn đã nói vectơ mutexes và bạn thay đổi kích cỡ vectơ để thêm các mutex mới, thì mutex cần được di chuyển từ một mảng đến người khác. Điểm chính thứ hai tôi muốn truyền đạt là. Trong hầu hết các trường hợp, đối tượng mutex phải là toàn cục. Điểm chính thứ ba mà tôi muốn truyền đạt là đi qua một mutex xung quanh bằng cách tham chiếu là yêu cầu một điều kiện chủng tộc – Curious

+0

Cảm ơn bạn! Tôi có thể đăng bounty bây giờ hhahaha. Nhưng trở lại điểm thứ ba. Cá nhân tôi đã viết mã này trong dự án đồng thời đầu tiên của tôi nên tôi đang nói từ kinh nghiệm. Tôi đã thiết lập mã sau đây. Trong một chủ đề nếu có quyền truy cập vào một khối đĩa không có một mutex liên kết với nó, tôi đã sử dụng một mẫu nhà máy để cho tôi một đối tượng mutex mới và sau đó truyền nó tới constructor thread làm tham chiếu. Sau đó tôi tiến hành tách sợi chỉ ra. Điều này do đó gây ra một cuộc đua trong sự hủy diệt của mutex và tôi đã không hạnh phúc: (Hy vọng rằng giải quyết nghi ngờ của bạn tốt :) – Curious

Các vấn đề liên quan