2009-08-02 27 views
6

Nếu một mutex được xác định trong một hàm, khóa của nó có áp dụng cho các hàm được gọi từ hàm đó không? tức làKhóa trên mutex có áp dụng cho các chức năng được gọi không?

void f() { 
    Mutex mutex; 
    g(); 
} 

Khóa vẫn áp dụng cho bất kỳ sửa đổi dữ liệu nào trong g()?

Ngoài ra, tôi có quyền nói rằng một khóa được định nghĩa trong một phương thức lớp học sẽ chỉ áp dụng cho các trường hợp cụ thể của lớp đó? Ý nghĩa:

Class Foo; 
Foo foo1, foo2; 
(In thread 1) foo1.bar(); 
(In thread 2) foo2.bar(); 

Mỗi cuộc gọi có thể xảy ra đồng thời không?

Sẽ là một phần thưởng tuyệt vời nếu ai đó có thể giải thích/chỉ ra các liên kết giải thích cơ chế đằng sau mutexes. Cảm ơn! Tôi hiện đang làm việc với thư viện Thread Qt, nếu thông tin đó giúp.

Trả lời

15

Trong ví dụ của bạn, bạn không thực sự khóa mutex, do đó, nó sẽ không ngăn các luồng khác nhau truy cập chức năng cùng một lúc. Ngoài ra, bạn khai báo mutex cục bộ bên trong hàm, để mỗi cuộc gọi hàm sử dụng một đối tượng mutex cục bộ khác. Ngay cả khi mutex này sẽ bị khóa, mỗi cuộc gọi chức năng sẽ khóa một đối tượng mutex khác, không ngăn cản truy cập đồng thời.

Một chiến lược tốt hơn sẽ là một thiết lập như thế này:

class A { 
    QMutex mutex; 

    void f() { 
    QMutexLocker ml(mutex); // Acquire a lock on mutex 
    g(); 

    // The lock on the mutex will be released when ml is destroyed. 
    // This happens at the end of this function. 
    } 

    // ... 
}; 

Trong trường hợp này mutex bị khóa càng lâu càng ml tồn tại, vì vậy cũng trong thời gian thread dành bên g(). Nếu một chuỗi khác sẽ gọi f() trong thời gian này, nó sẽ chặn trong việc tạo đối tượng ml cho đến khi chuỗi đầu tiên rời khỏi hàm và chuỗi mới có thể lấy khóa trên mutex.

+1

+1 sử dụng QT api –

7

Một mutex là một cái gì đó bạn lấy, và sẽ ngăn chặn bất kỳ chủ đề khác cố gắng lấy nó cho đến khi bạn phát hành nó từ chủ đề grabbing.

Trong câu hỏi của bạn, bạn có một hàm f phân bổ một cá thể Mutex. Đó là không đủ để khóa nó. Bạn phải gọi cụ thể mutex.lock() (trong Qt, nhưng cũng nói chung, trừ khi bạn sử dụng pthread, trong trường hợp đó sử dụng pthread_mutex_lock và vui chơi với các công cụ phụ thuộc nền tảng thấp, phụ thuộc vào nền tảng. Qt tóm tắt nó rất tốt).

đây là một ví dụ với Qt

void MyClass::doStuff(int c) 
    { 
     mutex.lock(); 
     a = c; 
     b = c * 2; 
     mutex.unlock(); 
    } 

khi bạn nhận được khóa, các cuộc gọi đến g() sẽ được thực hiện từ thread ai có khóa, vì vậy nó sẽ ở một mình ở chỗ gọi giả mà bạn không gọi g() từ các chủ đề khác từ một phần khác của mã. Khóa không có nghĩa là nó sẽ dừng tất cả các chủ đề khác. Nó sẽ ngăn chặn các chủ đề cố gắng để có được cùng một khóa, cho đến khi khóa được phát hành.

Nếu đó là cách duy nhất để các chủ đề của bạn đạt được g(), thì bạn được đồng bộ hóa trên quyền truy cập đó.

Đối với phần thứ hai của câu hỏi của bạn, Nếu mutex là một thuộc tính instance, thì chúng sẽ là hai mutex khác nhau. Bạn sẽ phải khai báo và khởi tạo một cá thể mutex lớp và tham chiếu đến nó cho khóa của bạn. Trong trường hợp đó, bất kỳ nỗ lực nào để gọi một phương thức trong lớp mà khóa lớp mutex sẽ được đồng bộ hóa một cách hiệu quả, nghĩa là không có hai luồng nào sẽ thực thi phương thức đó với nhau.

Ví dụ (Tôi không có Qt, vì vậy tôi không thể biên dịch mã này, và tôi dừng lại mã hóa với nó cách đây 2 năm, vì vậy nó không thể làm việc)

class Foo { 
public: 
    void method(void) { 
     mutex.lock(); 
     cout << "method called"; 
     // long computation 
     mutex.unlock(); 
    } 

private: 
    QMutex mutex; 
}; 

Ok, trong trường hợp này, giả sử bạn có hai luồng, 1 và 2 và hai trường hợp của lớp Foo, a và b. Giả sử rằng thread 1 gọi a.method() và thread 2 gọi b.method(). Trong trường hợp này, hai mutex là các cá thể khác nhau, do đó, mỗi luồng sẽ thực hiện cuộc gọi, độc lập và chạy song song.

Giả sử bạn có hai luồng, 1 và 2 và một phiên bản của lớp Foo được chia sẻ giữa hai luồng. nếu thread 1 gọi a.method() và sau đó thread 2 gọi a.method(), chuỗi 2 sẽ dừng lại và chờ cho đến khi khóa mutex được giải phóng.

Cuối cùng,

class Foo { 
public: 
    void method(void) { 
     mutex.lock(); 
     cout << "method called"; 
     // long computation 
     mutex.unlock(); 
    } 

private: 
    static QMutex mutex; 
}; 

QMutex Foo::mutex; 

Trong trường hợp này, các mutex là một biến tĩnh lớp. Bạn chỉ có một thể hiện của mutex cho mỗi cá thể đối tượng. Giả sử bạn có tình huống tương tự như trường hợp đầu tiên ở trên: hai luồng và hai trường hợp. Trong trường hợp này, khi luồng thứ hai cố gắng gọi b.method(), nó sẽ phải chờ a.method() được hoàn thành bởi luồng đầu tiên, vì khóa bây giờ là duy nhất và được chia sẻ trong tất cả các phiên bản của lớp của bạn.

Mọi chi tiết, Qt có một hướng dẫn tốt đẹp về đa luồng

https://doc.qt.io/qt-5/threads.html

+0

xin lỗi, tôi đã thực hiện một nỗ lực thực sự xấu ở mã giả. Chỉ cần sửa nó. Câu trả lời của bạn cho phần đầu tiên là chính xác những gì tôi đã tự hỏi, cảm ơn! – int3

2

mutex của bạn được instatiated tại địa phương, trên stack. Vì vậy, một cuộc gọi đến f() từ một sợi sẽ khóa riêng của phiên bản của mutex này. Bất kỳ lệnh gọi nào khác tới f() từ một luồng khác sẽ khóa riêng nó. Vì vậy, một điều kiện chủng tộc có thể xảy ra với dữ liệu được truy cập từ g()! Thậm chí khó khăn bạn gọi nó trên cùng một cá thể lớp:

MyClass foo; 
(In thread 1) foo->f(); 
(In thread 2) foo->f(); 

Cách xử lý khóa tốt hơn tùy thuộc vào những gì bạn muốn làm. Theo những gì bạn nói, tôi đoán chính sách tốt hơn là sửa đổi g() thực hiện trực tiếp: nó phải khóa một mutex được khai báo là global, hoặc là static trong g() để được chia sẻ giữa bất kỳ lệnh gọi nào với g(). Miễn là tôi hiểu bạn muốn khóa dữ liệu của mình trên toàn cầu?

+0

vì vậy nếu tôi có dữ liệu tôi muốn khóa trong các trường hợp cụ thể của Foo :: f(), tôi có nên khởi tạo mutex dưới dạng dữ liệu lớp không tĩnh không? – int3

+0

Vâng tôi đoán nó nên làm các trick. Bất kỳ cuộc gọi từ 'foo' thể hiện từ bất kỳ chủ đề trong ví dụ của tôi sẽ ngăn chặn truy cập đồng thời. (Nhưng không phải từ 2 trường hợp khác nhau của khóa học) –

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