2011-01-07 29 views
6

Từ hiểu biết của tôi về từ khóa có thể thay đổi, một trong những cách sử dụng chính của nó là lưu vào bộ nhớ đệm dữ liệu và tính toán chúng khi cần. Vì chúng có thể thay đổi (mặc dù chúng là const) nên nó không an toàn hoặc vô nghĩa khi sử dụng chúng? Phần lưu trữ sửa đổi dữ liệu do đó sẽ cần phải là một khóa và từ sự hiểu biết của tôi khi bạn viết cho đa luồng dữ liệu KHÔNG BAO GIỜ thay đổi và các bản sao nên được thực hiện và trả về/xích lại với nhau.Từ khóa C++ có thể thay đổi không hợp lệ cho chủ đề?

Vì vậy, nó là vô nghĩa hay xấu khi sử dụng từ khóa có thể thay đổi của C++?

+2

Tôi có thể nói bạn thực sự hiểu nhầm từ khóa 'có thể thay đổi '. Bạn lấy ý tưởng này ở đâu để ảnh hưởng đến bộ nhớ đệm dữ liệu? –

+3

@ Jonathan: Caching dữ liệu là việc sử dụng rất phổ biến của các thành viên dữ liệu 'mutable'. Nếu bạn có một hàm thành viên thực hiện một số nhiệm vụ tốn kém và bạn biết rằng hàm này được gọi thường xuyên, một tối ưu hóa điển hình là giới thiệu một thành viên dữ liệu có thể thay đổi để lưu trữ kết quả của tác vụ tốn kém để các cuộc gọi trong tương lai tới chức năng thành viên đó nhanh hơn . –

+5

Có, nhưng đó là trường hợp sử dụng, không phải là ý nghĩa của từ khóa. –

Trả lời

15

Vì vậy, nó là vô nghĩa hay xấu khi sử dụng từ khóa có thể thay đổi của C++?

Không; từ khóa mutable là Điều tốt. mutable có thể được sử dụng để tách trạng thái quan sát được của một đối tượng khỏi nội dung bên trong của đối tượng.

Với ví dụ "dữ liệu được lưu trong bộ nhớ cache" mà bạn mô tả (sử dụng phổ biến là mutable), nó cho phép lớp thực hiện tối ưu hóa "trong phần" không thực sự sửa đổi trạng thái quan sát được.

Đối với việc truy cập đối tượng từ nhiều chủ đề, có, bạn phải cẩn thận. Nói chung, nếu một lớp được thiết kế để được truy cập từ nhiều luồng và nó có các biến số mutable, nó sẽ đồng bộ hóa việc sửa đổi các biến đó trong nội bộ. Tuy nhiên, lưu ý rằng vấn đề thực sự là vấn đề quan trọng hơn. Thật dễ dàng để suy luận rằng:

  1. Tất cả các chủ đề của tôi chỉ gọi hàm thành viên const trên đối tượng chia sẻ này
  2. chức năng thành viên Const không sửa đổi các đối tượng trên mà chúng được gọi là
  3. Nếu một đối tượng không được sửa đổi , tôi không cần phải đồng bộ hóa truy cập vào nó
  4. vì vậy, tôi không cần phải đồng bộ hóa quyền truy cập vào đối tượng này

Lập luận này là sai vì (2) là sai: hàm thành viên const có thể thực sự modif y thành viên dữ liệu có thể thay đổi. Vấn đề là nó thực sự, thực sự dễ dàng để nghĩ rằng lập luận này là đúng. Giải pháp cho vấn đề này không phải là dễ dàng: hiệu quả, bạn chỉ phải cực kỳ cẩn thận khi viết mã đa luồng và chắc chắn rằng bạn hiểu cách thức các đối tượng được chia sẻ giữa các chủ đề được thực hiện hoặc những gì đảm bảo đồng thời.

+1

Nếu một lớp được thiết kế để được truy cập từ nhiều luồng và nó có * bất kỳ * biến không liên tục nào, nó cần đồng bộ hóa sửa đổi các biến đó. –

+2

@Charles: Vâng, tôi nghĩ có thể lập luận rằng đó là vấn đề với các thành viên dữ liệu 'có thể thay đổi '. Nếu bạn có một số luồng với tham chiếu const đến một đối tượng được chia sẻ, thường là hợp lý để giả định rằng bạn có thể gọi các hàm thành viên const đó mà không cần bất kỳ đồng bộ hóa nào. Thật dễ dàng để quên đi các hàm thành viên có thể biến đổi và không chính xác mà không thể thay đổi trạng thái cơ bản. –

+0

+1 cho câu trả lời rõ ràng, chính xác và hợp lý! – Nawaz

2

Không, từ khóa có thể thay đổi để bạn có thể thay đổi ngay cả khi đối tượng là const, chẳng hạn như siêu dữ liệu không phải là thuộc tính của đối tượng mà là quản lý của nó. v.v.) Nó không có gì để làm với luồng.

+6

Đang đọc _is_ một vấn đề ở đây. Nếu bạn có một số luồng, tất cả với tham chiếu const cho một số đối tượng được chia sẻ, thật dễ dàng để giả định rằng tất cả các luồng đó có thể gọi hàm thành viên const trên đối tượng đó mà không có bất kỳ kiểu đồng bộ nào. Tuy nhiên, nếu có các thành viên dữ liệu có thể thay đổi được sửa đổi bởi các hàm thành viên const đó, bạn phải đảm bảo rằng những sửa đổi đó được đồng bộ hóa chính xác. –

+1

Điều tôi muốn nói là, chính từ khóa không liên quan gì đến luồng, cách thức giống như 'biến động'. Và tôi * không bao giờ * giả sử rằng các phương thức gọi trên một đối tượng const từ hai luồng hoạt động - không phải vì điều này, nhưng vì thực tế là trong khi 'const' chỉ có nghĩa là đối tượng không thay đổi, nó không nói bất cứ điều gì về các mục tiêu của bất kỳ hoặc tham chiếu nào có thể có - vì vậy bạn có thể sửa đổi điều gì đó không chính xác ngay cả thông qua tham chiếu const mà không có dữ liệu có thể thay đổi. – Mehrdad

+0

Bạn đang sai về mutable, nó cho phép các thành viên vars đánh dấu có thể thay đổi được sửa đổi trong các phương pháp được đánh dấu const, bất kể nếu dụ họ nhận được gọi là const hay không. –

7

Trên cuối đối diện, hầu hết các mã đa luồng của tôi đòi hỏi việc sử dụng các mutable keyword:

class object { 
    type m_data; 
    mutable mutex m_mutex; 
public: 
    void set(type const & value) { 
     scoped_lock lock(m_mutex); 
     m_data = value; 
    } 
    type get() const { 
     scoped_lock lock(m_mutex); 
     return m_data; 
    } 
}; 

Thực tế là phương pháp get không thay đổi trạng thái của object được khai báo bằng phương tiện từ khóa const.Nhưng nếu không có công cụ sửa đổi mutable được áp dụng cho việc khai báo thuộc tính mutex, mã sẽ không thể khóa hoặc giải phóng hoạt động mutex - rõ ràng sửa đổi số mutex, ngay cả khi chúng không sửa đổi object.

Bạn thậm chí có thể làm cho thuộc tính data có thể thay đổi nếu nó có thể được đánh giá lazily và chi phí cao, miễn là bạn khóa đối tượng. Đây là sử dụng bộ nhớ cache mà bạn tham chiếu trong câu hỏi.

Công cụ sửa đổi mutable không phải là vấn đề với mã đa luồng, chỉ khi bạn cố gắng khóa ít hơn đa luồng. Và như với tất cả các chương trình khóa-ít hơn, bạn phải là rất cẩn thận với những gì bạn làm, bất kể const hoặc mutable. Bạn có thể viết mã đa luồng hoàn toàn không an toàn gọi các phương thức const trên các đối tượng không có thuộc tính mutable. Ví dụ đơn giản sẽ loại bỏ các mutex từ mã trước đó và có N chủ đề thực hiện chỉ get() s trong khi một thread khác thực hiện set() s. Thực tế là get() là const không đảm bảo rằng bạn sẽ không nhận được kết quả không hợp lệ nếu một luồng khác đang sửa đổi.

+0

Nó sẽ không an toàn để không khóa get()? Cách duy nhất tôi có thể thấy m_data là không hợp lệ là nếu m_data xảy ra là> 32 hoặc 64 bit và nó phải làm một số nhiệm vụ để sao chép đối tượng hoàn chỉnh. Đây có phải là lý do duy nhất tại sao? nếu m_data là một con trỏ sau đó tôi không thấy bất kỳ lý do để khóa trong có được. –

+0

@ acidzombie24: Tất cả phụ thuộc vào kiến ​​trúc bạn đang làm việc và một loạt các yếu tố khác, nguyên tử của lần đọc/ghi không được thực thi bởi ngôn ngữ. Nếu 'type' là một cấu trúc chứa 4 ký tự thì phần tử đọc nhiều nhất có thể không phải là nguyên tử. Ngay cả khi nó là một giá trị 32 bit duy nhất trong một bộ xử lý intel hiện đại, đọc có thể không nguyên tử nếu dữ liệu (trong trường hợp này 'đối tượng') không phải là từ phù hợp. Một lần nữa, khi viết mã không có mã khóa, bạn cần phải tính đến * nhiều thứ *. –

+0

Đối với ví dụ cụ thể của bạn, tôi sẽ đề xuất một khóa ReadWrite [link] (https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock) – Niki

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