Tại sao các hàm biến điều kiện của pthreads yêu cầu một mutex?


182

Tôi đang đọc lên pthread.h; các hàm điều kiện liên quan đến biến điều kiện (như pthread_cond_wait(3)) yêu cầu một mutex làm đối số. Tại sao? Theo như tôi có thể nói, tôi sẽ tạo ra một mutex chỉ để sử dụng như đối số đó? Mutex đó phải làm gì?

Câu trả lời:


194

Đó chỉ là cách các biến điều kiện (hoặc ban đầu) được triển khai.

Mutex được sử dụng để bảo vệ chính biến điều kiện . Đó là lý do tại sao bạn cần khóa nó trước khi bạn chờ đợi.

Chờ đợi sẽ "nguyên tử" mở khóa mutex, cho phép người khác truy cập vào biến điều kiện (để báo hiệu). Sau đó, khi biến điều kiện được báo hiệu hoặc phát đến, một hoặc nhiều luồng trong danh sách chờ sẽ được đánh thức và mutex sẽ bị khóa một cách kỳ diệu cho luồng đó.

Bạn thường thấy thao tác sau với các biến điều kiện, minh họa cách chúng hoạt động. Ví dụ sau đây là một luồng công nhân được đưa ra công việc thông qua tín hiệu đến một biến điều kiện.

thread:
    initialise.
    lock mutex.
    while thread not told to stop working:
        wait on condvar using mutex.
        if work is available to be done:
            do the work.
    unlock mutex.
    clean up.
    exit thread.

Công việc được thực hiện trong vòng lặp này với điều kiện là có sẵn khi chờ đợi trở lại. Khi luồng đã được gắn cờ để dừng thực hiện công việc (thường bởi một luồng khác thiết lập điều kiện thoát sau đó khởi động biến điều kiện để đánh thức luồng này), vòng lặp sẽ thoát, mutex sẽ được mở khóa và luồng này sẽ thoát.

Mã ở trên là một mô hình người tiêu dùng duy nhất vì mutex vẫn bị khóa trong khi công việc đang được thực hiện. Đối với một biến thể nhiều người tiêu dùng, bạn có thể sử dụng, làm ví dụ :

thread:
    initialise.
    lock mutex.
    while thread not told to stop working:
        wait on condvar using mutex.
        if work is available to be done:
            copy work to thread local storage.
            unlock mutex.
            do the work.
            lock mutex.
    unlock mutex.
    clean up.
    exit thread.

cho phép người tiêu dùng khác nhận được công việc trong khi người này đang làm việc.

Biến điều kiện giúp bạn giảm bớt gánh nặng của việc bỏ phiếu một số điều kiện thay vì cho phép một luồng khác thông báo cho bạn khi có điều gì đó cần xảy ra. Một chủ đề khác có thể nói rằng chủ đề đó có sẵn như sau:

lock mutex.
flag work as available.
signal condition variable.
unlock mutex.

Đại đa số những gì thường được gọi là đánh thức giả thường nói chung luôn luôn là do nhiều luồng đã được báo hiệu trong phạm vi của chúng pthread_cond_wait cuộc gọi (phát sóng), một người sẽ quay lại với mutex, thực hiện công việc, sau đó chờ lại.

Sau đó, luồng tín hiệu thứ hai có thể đi ra khi không có việc gì phải làm. Vì vậy, bạn phải có thêm một biến chỉ ra rằng công việc nên được thực hiện (điều này vốn đã được bảo vệ bằng mutex với cặp condvar / mutex ở đây - các luồng khác cần thiết để khóa mutex trước khi thay đổi nó).

Đó về mặt kỹ thuật có thể cho một thread để trở về từ một chờ đợi điều kiện mà không bị đuổi bởi một tiến trình khác (đây là một wakeup giả mạo chính hãng) nhưng, trong tất cả nhiều năm tôi làm việc trên pthreads, cả về phát triển / dịch vụ của mã và như một người dùng trong số họ, tôi chưa bao giờ nhận được một trong số này. Có lẽ đó chỉ là do HP đã triển khai tốt :-)

Trong mọi trường hợp, cùng một mã xử lý trường hợp sai sót cũng xử lý các đánh thức giả mạo chính hãng cũng vì cờ có sẵn cho công việc sẽ không được đặt cho những trường hợp đó.


3
'làm điều gì đó' không nên ở trong vòng lặp while. Bạn muốn vòng lặp while của mình chỉ kiểm tra điều kiện, nếu không bạn cũng có thể 'làm gì đó' nếu bạn nhận được một sự thức tỉnh giả.
số

1
không, xử lý lỗi là thứ hai này. Với pthreads, bạn có thể thức dậy, không có lý do rõ ràng (một đánh thức giả) và không có bất kỳ lỗi nào. Do đó, bạn cần kiểm tra lại 'một số điều kiện' sau khi bạn thức dậy.
số

1
Tôi không chắc là tôi hiểu. Tôi đã có phản ứng tương tự như nos ; Tại sao do somethingbên trong whilevòng lặp?
ELLIOTTCABLE

1
Có lẽ tôi không làm cho nó đủ rõ ràng. Vòng lặp không phải là chờ công việc sẵn sàng để bạn có thể làm điều đó. Vòng lặp là vòng lặp công việc "vô hạn" chính. Nếu bạn trở về từ cond_wait và cờ công việc được đặt, bạn thực hiện công việc sau đó lặp lại một lần nữa. "trong khi một số điều kiện" sẽ chỉ sai khi bạn muốn luồng ngừng hoạt động tại thời điểm nó sẽ giải phóng mutex và rất có thể thoát.
paxdiablo

7
@stefaanv "mutex vẫn là để bảo vệ biến điều kiện, không có cách nào khác để bảo vệ nó": mutex không phải là để bảo vệ biến điều kiện; đó là để bảo vệ dữ liệu vị ngữ , nhưng tôi nghĩ bạn biết rằng từ việc đọc bình luận của bạn đã tuân theo tuyên bố đó. Bạn có thể báo hiệu một biến điều kiện một cách hợp pháp và được hỗ trợ đầy đủ bằng cách triển khai, hậu khóa của mutex bao bọc vị ngữ và trong thực tế, bạn sẽ giảm bớt sự tranh chấp trong một số trường hợp.
WhozCraig

59

Một biến điều kiện khá hạn chế nếu bạn chỉ có thể báo hiệu một điều kiện, thông thường bạn cần xử lý một số dữ liệu liên quan đến điều kiện được báo hiệu. Việc báo hiệu / đánh thức phải được thực hiện một cách nguyên tử để đạt được điều đó mà không đưa ra các điều kiện chủng tộc, hoặc quá phức tạp

pthreads cũng có thể cung cấp cho bạn, vì lý do kỹ thuật, một sự thức tỉnh giả . Điều đó có nghĩa là bạn cần kiểm tra một vị ngữ, vì vậy bạn có thể chắc chắn tình trạng thực sự đã được báo hiệu - và phân biệt điều đó với một đánh thức giả. Kiểm tra một điều kiện như vậy liên quan đến việc chờ đợi nó cần được bảo vệ - vì vậy một biến điều kiện cần một cách để chờ / thức dậy nguyên tử trong khi khóa / mở khóa một mutex bảo vệ điều kiện đó.

Hãy xem xét một ví dụ đơn giản khi bạn được thông báo rằng một số dữ liệu được tạo ra. Có thể một luồng khác đã tạo một số dữ liệu mà bạn muốn và đặt một con trỏ tới dữ liệu đó.

Hãy tưởng tượng một luồng sản xuất cung cấp một số dữ liệu cho một luồng tiêu dùng khác thông qua một con trỏ 'some_data'.

while(1) {
    pthread_cond_wait(&cond); //imagine cond_wait did not have a mutex
    char *data = some_data;
    some_data = NULL;
    handle(data);
}

Bạn tự nhiên nhận được rất nhiều điều kiện cuộc đua, điều gì sẽ xảy ra nếu luồng khác đã làm some_data = new_datangay sau khi bạn thức dậy, nhưng trước khi bạn làmdata = some_data

Bạn thực sự không thể tạo mutex của riêng bạn để bảo vệ trường hợp này .eg

while(1) {

    pthread_cond_wait(&cond); //imagine cond_wait did not have a mutex
    pthread_mutex_lock(&mutex);
    char *data = some_data;
    some_data = NULL;
    pthread_mutex_unlock(&mutex);
    handle(data);
}

Sẽ không hoạt động, vẫn có cơ hội xảy ra tình trạng chạy đua giữa lúc thức dậy và chộp lấy mutex. Đặt mutex trước pthread_cond_wait không giúp ích gì cho bạn, vì bây giờ bạn sẽ giữ mutex trong khi chờ đợi - tức là nhà sản xuất sẽ không bao giờ có thể lấy được mutex. (lưu ý, trong trường hợp này bạn có thể tạo biến điều kiện thứ hai để báo hiệu cho nhà sản xuất rằng bạn đã hoàn thànhsome_data - mặc dù điều này sẽ trở nên phức tạp, đặc biệt là nếu bạn muốn nhiều nhà sản xuất / người tiêu dùng.)

Vì vậy, bạn cần một cách để giải phóng nguyên tử / lấy mutex khi chờ đợi / thức dậy từ điều kiện. Đó là những gì biến điều kiện pthread làm, và đây là những gì bạn làm:

while(1) {
    pthread_mutex_lock(&mutex);
    while(some_data == NULL) { // predicate to acccount for spurious wakeups,would also 
                               // make it robust if there were several consumers
       pthread_cond_wait(&cond,&mutex); //atomically lock/unlock mutex
    }

    char *data = some_data;
    some_data = NULL;
    pthread_mutex_unlock(&mutex);
    handle(data);
}

(nhà sản xuất đương nhiên sẽ cần phải thực hiện các biện pháp phòng ngừa tương tự, luôn bảo vệ 'some_data' với cùng một mutex và đảm bảo rằng nó không ghi đè some_data nếu hiện tại some_data! = NULL)


Không phải while (some_data != NULL)là một vòng lặp do-while để nó chờ biến điều kiện ít nhất một lần?
Thẩm phán Maygarden

3
Không. Điều bạn thực sự chờ đợi là 'some_data' không phải là null. Nếu nó không phải là "lần đầu tiên", thật tuyệt, bạn đang giữ mutex và có thể sử dụng dữ liệu một cách an toàn. Nếu bạn có vòng lặp do / while, bạn sẽ bỏ lỡ thông báo nếu có ai đó báo hiệu biến điều kiện trước khi bạn chờ đợi (không có gì giống như các sự kiện được tìm thấy trên win32 mà vẫn báo hiệu cho đến khi ai đó đợi họ)
nos

4
Tôi chỉ vấp phải câu hỏi này và thẳng thắn, thật kỳ lạ khi thấy rằng câu trả lời này, đúng, có rất ít điểm so với câu trả lời của paxdiablo có những sai sót nhất định (vẫn cần nguyên tử, mutex chỉ cần xử lý điều kiện, không xử lý hoặc thông báo). Tôi đoán đó chỉ là cách stackoverflow hoạt động ...
stefaanv

@stefaanv, nếu bạn muốn nêu chi tiết sai sót, như nhận xét cho câu trả lời của tôi để tôi thấy chúng kịp thời, thay vì vài tháng sau :-), tôi sẽ vui lòng sửa chúng. Các cụm từ ngắn gọn của bạn không thực sự cung cấp cho tôi đủ chi tiết để tìm ra những gì bạn đang cố gắng nói.
paxdiablo

1
@nos, không while(some_data != NULL)nên while(some_data == NULL)?
Eric Z

30

Các biến điều kiện POSIX là không trạng thái. Vì vậy, trách nhiệm của bạn là duy trì nhà nước. Vì trạng thái sẽ được truy cập bởi cả hai luồng chờ và luồng xử lý các luồng khác dừng chờ, nên nó phải được bảo vệ bởi một mutex. Nếu bạn nghĩ rằng bạn có thể sử dụng các biến điều kiện mà không có mutex, thì bạn đã không nắm bắt được rằng các biến điều kiện là không trạng thái.

Các biến điều kiện được xây dựng xung quanh một điều kiện. Chủ đề chờ trên một biến điều kiện đang chờ một số điều kiện. Chủ đề mà các biến điều kiện tín hiệu thay đổi điều kiện đó. Ví dụ, một luồng có thể đang chờ một số dữ liệu đến. Một số chủ đề khác có thể nhận thấy rằng dữ liệu đã đến. "Dữ liệu đã đến" là điều kiện.

Đây là cách sử dụng cổ điển của một biến điều kiện, được đơn giản hóa:

while(1)
{
    pthread_mutex_lock(&work_mutex);

    while (work_queue_empty())       // wait for work
       pthread_cond_wait(&work_cv, &work_mutex);

    work = get_work_from_queue();    // get work

    pthread_mutex_unlock(&work_mutex);

    do_work(work);                   // do that work
}

Xem làm thế nào các chủ đề đang chờ làm việc. Công việc được bảo vệ bởi một mutex. Chờ đợi giải phóng mutex để một luồng khác có thể cung cấp cho chủ đề này một số công việc. Đây là cách nó sẽ được báo hiệu:

void AssignWork(WorkItem work)
{
    pthread_mutex_lock(&work_mutex);

    add_work_to_queue(work);           // put work item on queue

    pthread_cond_signal(&work_cv);     // wake worker thread

    pthread_mutex_unlock(&work_mutex);
}

Lưu ý rằng bạn cần mutex để bảo vệ hàng đợi công việc. Lưu ý rằng chính biến điều kiện không biết liệu có hoạt động hay không. Nghĩa là, một biến điều kiện phải được liên kết với một điều kiện, điều kiện đó phải được duy trì bởi mã của bạn và vì nó được chia sẻ giữa các luồng, nên nó phải được bảo vệ bởi một mutex.


1
Hoặc, nói một cách chính xác hơn, toàn bộ điểm của các biến điều kiện là cung cấp một hoạt động "mở khóa và chờ đợi" nguyên tử. Không có mutex, sẽ không có gì để mở khóa.
David Schwartz

Bạn có thể giải thích ý nghĩa của không quốc tịch ?
snr

@snr Họ không có nhà nước nào. Chúng không bị "khóa" hoặc "báo hiệu" hoặc "không dấu". Vì vậy, trách nhiệm của bạn là theo dõi bất kỳ trạng thái nào có liên quan đến biến điều kiện. Ví dụ, nếu biến điều kiện cho phép một luồng biết khi nào hàng đợi trở nên trống rỗng, thì đó phải là trường hợp một luồng có thể làm cho hàng đợi không trống và một số luồng khác cần biết khi hàng đợi trở nên trống rỗng. Đó là trạng thái được chia sẻ và bạn phải bảo vệ nó bằng một mutex. Bạn có thể sử dụng biến điều kiện, kết hợp với trạng thái chia sẻ được bảo vệ bởi một mutex, làm cơ chế đánh thức.
David Schwartz

16

Không phải tất cả các hàm biến điều kiện đều yêu cầu một mutex: chỉ các hoạt động chờ thực hiện. Các hoạt động tín hiệu và phát sóng không yêu cầu một mutex. Một biến điều kiện cũng không liên quan vĩnh viễn với một mutex cụ thể; các mutex bên ngoài không bảo vệ các biến điều kiện. Nếu một biến điều kiện có trạng thái bên trong, chẳng hạn như một chuỗi các luồng chờ, thì điều này phải được bảo vệ bằng một khóa bên trong bên trong biến điều kiện.

Các hoạt động chờ kết hợp một biến điều kiện và một mutex, bởi vì:

  • một luồng đã khóa mutex, đánh giá một số biểu thức trên các biến được chia sẻ và thấy nó là sai, do đó nó cần phải chờ.
  • luồng phải chuyển nguyên tử từ việc sở hữu mutex, sang chờ với điều kiện.

Vì lý do này, hoạt động chờ đợi làm đối số cả mutex và condition: để nó có thể quản lý việc chuyển nguyên tử của một luồng từ sở hữu mutex sang chờ, để luồng không trở thành nạn nhân của điều kiện cuộc đua đánh thức bị mất .

Một điều kiện cuộc đua đánh thức bị mất sẽ xảy ra nếu một luồng từ bỏ một mutex, và sau đó chờ đợi một đối tượng đồng bộ hóa không trạng thái, nhưng theo cách không phải là nguyên tử: tồn tại một cửa sổ thời gian khi luồng không còn khóa và có chưa bắt đầu chờ đợi trên đối tượng. Trong cửa sổ này, một luồng khác có thể đi vào, làm cho điều kiện chờ đợi thành đúng, báo hiệu sự đồng bộ hóa không trạng thái và sau đó biến mất. Đối tượng không trạng thái không nhớ rằng nó đã được báo hiệu (nó không trạng thái). Vì vậy, chủ đề ban đầu đi ngủ trên đối tượng đồng bộ hóa không trạng thái và không thức dậy, mặc dù điều kiện mà nó cần đã trở thành sự thật: mất thức tỉnh.

Các hàm chờ biến điều kiện tránh đánh thức bị mất bằng cách đảm bảo rằng chuỗi cuộc gọi được đăng ký để bắt kịp thức dậy một cách đáng tin cậy trước khi nó từ bỏ mutex. Điều này là không thể nếu hàm chờ biến điều kiện không lấy mutex làm đối số.


Bạn có thể cung cấp tài liệu tham khảo rằng các hoạt động phát sóng không yêu cầu để có được mutex? Trên MSVC phát sóng bị bỏ qua.
xvan

@xvan POSIX pthread_cond_broadcastvà các pthread_cond_signalhoạt động (câu hỏi SO này là về) thậm chí không lấy mutex làm đối số; Chỉ điều kiện. Thông số POSIX ở đây . Mutex chỉ được đề cập trong tham chiếu đến những gì xảy ra trong các chuỗi chờ khi chúng thức dậy.
Kaz

Bạn có thể giải thích ý nghĩa của không quốc tịch ?
snr

1
@snr Một đối tượng đồng bộ hóa không trạng thái không nhớ bất kỳ trạng thái nào liên quan đến báo hiệu. Khi được báo hiệu, nếu một cái gì đó đang chờ đợi trên nó bây giờ, nó được đánh thức, nếu không thì thức dậy bị lãng quên. Các biến điều kiện là không trạng thái như thế này. Trạng thái cần thiết để làm cho đồng bộ hóa đáng tin cậy được duy trì bởi ứng dụng và được bảo vệ bởi mutex được sử dụng cùng với các biến điều kiện, theo logic được viết chính xác.
Kaz

7

Tôi không tìm thấy các câu trả lời khác ngắn gọn và dễ đọc như trang này . Thông thường mã chờ đợi trông giống như thế này:

mutex.lock()
while(!check())
    condition.wait()
mutex.unlock()

Có ba lý do để bọc wait()trong một mutex:

  1. không có mutex một chủ đề khác có thể signal()trướcwait() và chúng tôi sẽ bỏ lỡ sự thức dậy này.
  2. thông thường check()phụ thuộc vào sửa đổi từ một chủ đề khác, vì vậy dù sao bạn cũng cần loại trừ lẫn nhau.
  3. để đảm bảo rằng chuỗi ưu tiên cao nhất sẽ được tiến hành trước (hàng đợi cho mutex cho phép người lập lịch quyết định ai sẽ đi tiếp).

Điểm thứ ba không phải lúc nào cũng là một mối quan tâm - bối cảnh lịch sử được liên kết từ bài viết đến cuộc trò chuyện này .

Đánh thức giả thường được đề cập liên quan đến cơ chế này (tức là chuỗi chờ được đánh thức mà không signal()được gọi). Tuy nhiên, các sự kiện như vậy được xử lý bởi các vòng lặp check().


4

Các biến điều kiện được liên kết với một mutex bởi vì đó là cách duy nhất nó có thể tránh được cuộc đua mà nó được thiết kế để tránh.

// incorrect usage:
// thread 1:
while (notDone) {
    pthread_mutex_lock(&mutex);
    bool ready = protectedReadyToRunVariable
    pthread_mutex_unlock(&mutex);
    if (ready) {
        doWork();
    } else {
        pthread_cond_wait(&cond1); // invalid syntax: this SHOULD have a mutex
    }
}

// signalling thread
// thread 2:
prepareToRunThread1();
pthread_mutex_lock(&mutex);
   protectedReadyToRuNVariable = true;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond1);

Now, lets look at a particularly nasty interleaving of these operations

pthread_mutex_lock(&mutex);
bool ready = protectedReadyToRunVariable;
pthread_mutex_unlock(&mutex);
                                 pthread_mutex_lock(&mutex);
                                 protectedReadyToRuNVariable = true;
                                 pthread_mutex_unlock(&mutex);
                                 pthread_cond_signal(&cond1);
if (ready) {
pthread_cond_wait(&cond1); // uh o!

Tại thời điểm này, không có luồng nào sẽ báo hiệu biến điều kiện, vì vậy thread1 sẽ đợi mãi, mặc dù bảo vệReadyToRunVariable nói rằng nó đã sẵn sàng!

Cách duy nhất để giải quyết vấn đề này là các biến điều kiện sẽ giải phóng mutex một cách nguyên tử trong khi đồng thời bắt đầu chờ đợi biến điều kiện. Đây là lý do tại sao hàm cond_wait yêu cầu một mutex

// correct usage:
// thread 1:
while (notDone) {
    pthread_mutex_lock(&mutex);
    bool ready = protectedReadyToRunVariable
    if (ready) {
        pthread_mutex_unlock(&mutex);
        doWork();
    } else {
        pthread_cond_wait(&mutex, &cond1);
    }
}

// signalling thread
// thread 2:
prepareToRunThread1();
pthread_mutex_lock(&mutex);
   protectedReadyToRuNVariable = true;
   pthread_cond_signal(&mutex, &cond1);
pthread_mutex_unlock(&mutex);

3

Mutex được cho là bị khóa khi bạn gọi pthread_cond_wait ; Khi bạn gọi nó là nguyên tử, cả hai đều mở khóa mutex và sau đó chặn với điều kiện. Khi điều kiện được báo hiệu, nó sẽ khóa lại và trả lại.

Điều này cho phép thực hiện lập lịch dự đoán nếu muốn, trong đó luồng xử lý tín hiệu có thể đợi cho đến khi mutex được giải phóng để xử lý và sau đó báo hiệu điều kiện.


Vì vậy, có một lý do để tôi không chỉ để mutex luôn được mở khóa, và sau đó khóa nó ngay trước khi chờ đợi, và sau đó mở khóa nó ngay sau khi chờ kết thúc?
ELLIOTTCABLE

Mutex cũng giải quyết một số chủng tộc tiềm năng giữa các luồng chờ và tín hiệu. miễn là mutex luôn bị khóa khi thay đổi điều kiện và báo hiệu, bạn sẽ không bao giờ thấy mình bị mất tín hiệu và ngủ mãi mãi
Hasturkun

Vì vậy, trước tiên tôi nên chờ đợi trên mutex trên mutex của conditionvar, trước khi chờ đợi trên conditionvar? Tôi không chắc là tôi hiểu gì cả.
ELLIOTTCABLE

2
@elliottcable: Không giữ mutex, làm thế nào bạn có thể biết bạn nên hay không nên chờ đợi? Điều gì xảy ra nếu những gì bạn đang chờ đợi chỉ xảy ra?
David Schwartz

1

Tôi đã thực hiện một bài tập trong lớp nếu bạn muốn một ví dụ thực tế về biến điều kiện:

#include "stdio.h"
#include "stdlib.h"
#include "pthread.h"
#include "unistd.h"

int compteur = 0;
pthread_cond_t varCond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex_compteur;

void attenteSeuil(arg)
{
    pthread_mutex_lock(&mutex_compteur);
        while(compteur < 10)
        {
            printf("Compteur : %d<10 so i am waiting...\n", compteur);
            pthread_cond_wait(&varCond, &mutex_compteur);
        }
        printf("I waited nicely and now the compteur = %d\n", compteur);
    pthread_mutex_unlock(&mutex_compteur);
    pthread_exit(NULL);
}

void incrementCompteur(arg)
{
    while(1)
    {
        pthread_mutex_lock(&mutex_compteur);

            if(compteur == 10)
            {
                printf("Compteur = 10\n");
                pthread_cond_signal(&varCond);
                pthread_mutex_unlock(&mutex_compteur);
                pthread_exit(NULL);
            }
            else
            {
                printf("Compteur ++\n");
                compteur++;
            }

        pthread_mutex_unlock(&mutex_compteur);
    }
}

int main(int argc, char const *argv[])
{
    int i;
    pthread_t threads[2];

    pthread_mutex_init(&mutex_compteur, NULL);

    pthread_create(&threads[0], NULL, incrementCompteur, NULL);
    pthread_create(&threads[1], NULL, attenteSeuil, NULL);

    pthread_exit(NULL);
}

1

Nó dường như là một quyết định thiết kế cụ thể chứ không phải là một nhu cầu khái niệm.

Theo các tài liệu pthreads, lý do khiến mutex không bị tách rời là có sự cải thiện hiệu suất đáng kể bằng cách kết hợp chúng và họ hy vọng rằng vì điều kiện chủng tộc thông thường nếu bạn không sử dụng mutex, dù sao thì nó cũng sẽ luôn được thực hiện.

https://linux.die.net/man/3/pthread_cond_wait

Các tính năng của Mutexes và Biến điều kiện

Nó đã được đề xuất rằng việc mua lại và phát hành mutex được tách rời khỏi điều kiện chờ đợi. Điều này đã bị từ chối vì đó là bản chất kết hợp của hoạt động, trên thực tế, tạo điều kiện cho việc triển khai thời gian thực. Những triển khai đó về nguyên tử có thể di chuyển một luồng ưu tiên cao giữa biến điều kiện và mutex theo cách trong suốt đối với người gọi. Điều này có thể ngăn chặn các chuyển đổi ngữ cảnh bổ sung và cung cấp khả năng thu được một mutex xác định hơn khi luồng chờ được báo hiệu. Do đó, các vấn đề công bằng và ưu tiên có thể được giải quyết trực tiếp bằng kỷ luật lập lịch. Hơn nữa, điều kiện chờ hoạt động hiện tại phù hợp với thực tế hiện có.


0

Có hàng tấn exeges về điều đó, nhưng tôi muốn tóm tắt nó bằng một ví dụ sau đây.

1 void thr_child() {
2    done = 1;
3    pthread_cond_signal(&c);
4 }

5 void thr_parent() {
6    if (done == 0)
7        pthread_cond_wait(&c);
8 }

Có gì sai với đoạn mã? Chỉ cần suy ngẫm phần nào trước khi đi trước.


Vấn đề là thực sự tinh tế. Nếu cha mẹ gọi thr_parent()và sau đó bác bỏ giá trị của donenó, nó sẽ thấy rằng đó là 0và do đó cố gắng đi ngủ. Nhưng ngay trước khi nó gọi chờ đi ngủ, bố mẹ bị gián đoạn giữa các dòng 6-7 và con chạy. Đứa trẻ thay đổi biến trạng thái donethành 1và tín hiệu, nhưng không có luồng nào đang chờ và do đó không có luồng nào được đánh thức. Khi cha mẹ chạy lại, nó ngủ mãi, điều này thực sự rất nghiêm trọng.

Điều gì nếu chúng được thực hiện trong khi có được khóa riêng lẻ?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.