programing

pthreads의 조건 변수 함수에 뮤텍스가 필요한 이유는 무엇입니까?

copysource 2022. 7. 16. 13:36
반응형

pthreads의 조건 변수 함수에 뮤텍스가 필요한 이유는 무엇입니까?

는 지금 up i i에대 i i i 。pthread.h예: ; "CHANGE")pthread_cond_wait(3))는 mutex를 인수로 요구합니다.왜요?내가 아는 한, 내가 그 논쟁으로 사용하기 위해 뮤텍스를 만든다고?저 뮤텍스는 뭘 하려는 거죠?

이것은 조건 변수가 (또는 원래) 구현되는 방식일 뿐입니다.

뮤텍스는 조건 변수 자체를 보호하기 위해 사용됩니다.그래서 기다리기 전에 잠궈야 해

대기하면 뮤텍스가 "원자적으로" 잠금 해제되어 다른 사람이 (신호를 위해) 조건 변수에 액세스할 수 있습니다.그런 다음 조건 변수가 시그널링 또는 브로드캐스트되면 대기 목록에 있는 하나 이상의 스레드가 웨이크업되고 해당 스레드에 대해 뮤텍스가 다시 마법처럼 잠깁니다.

일반적으로 다음 연산과 조건 변수가 함께 표시되며, 이러한 연산이 어떻게 작동하는지 보여 줍니다.다음 예시는 신호를 통해 조건 변수에 작업이 제공되는 워커 스레드입니다.

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.

대기 시간이 돌아왔을 때 사용할 수 있는 것이 있는 경우 이 루프 내에서 작업이 수행됩니다.스레드가 작업을 중지하도록 플래그가 지정되면(보통 다른 스레드가 종료 조건을 설정한 후 조건 변수를 호출하여 이 스레드를 깨우는 방식으로), 루프가 종료되고 뮤텍스가 잠금 해제되고 스레드가 종료됩니다.

위의 코드는 작업 중에 뮤텍스가 잠긴 상태로 유지되므로 단일 소비자 모델입니다.다중 소비자 변동의 경우 를 들어 다음과 같이 사용할 수 있습니다.

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.

다른 소비자가 작업 중에 작업을 받을 수 있습니다.

condition 변수를 사용하면 어떤 조건을 폴링해야 하는 부담을 덜 수 있습니다.대신 다른 스레드에서 어떤 일이 발생할 때 알림을 받을 수 있습니다.다른 스레드는 다음과 같이 동작하는 스레드를 사용할 수 있음을 나타냅니다.

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

것의 으로 여러 가 항상 스플리어스 웨이크업 입니다.pthread_cond_wait콜(복수)을 하면 뮤텍스와 함께 돌아와 작업을 수행한 후 다시 대기합니다.

그러면 할 일이 없을 때 두 번째 신호탄이 나올 수 있습니다.따라서 작업을 수행할 필요가 있음을 나타내는 추가 변수가 필요합니다(이것은 본질적으로 condvar/mutex 쌍으로 보호되어 있습니다.단, mutex를 변경하기 전에 잠그는 데 필요한 다른 스레드입니다).

스레드가 다른 프로세스에 의해 기동되지 않고 상태 대기에서 돌아오는 것은 기술적으로 가능했습니다(이것은 진정한 스플리어스 웨이크업입니다).그러나 pthreads에 대해 오랜 시간 작업하면서 코드 개발/서비스와 사용자로서, 저는 이 중 하나를 받은 적이 없습니다.HP가 제대로 구현했기 때문일 수도 있습니다.-)

어느 경우든 이 오류를 처리한 동일한 코드에서는 실제 가짜 웨이크업도 처리됩니다.이는 work-available 플래그가 설정되지 않기 때문입니다.

조건 변수에는 어떤 조건만 시그널링할 수 있는 경우 매우 제한적이며, 일반적으로 시그널링된 조건과 관련된 일부 데이터를 처리해야 합니다.레이스 조건을 도입하지 않고 신호 전달/웨이크업을 원자적으로 실시하거나 지나치게 복잡해야 합니다.

pthreads는 또한 다소 기술적인 이유로 당신에게 가짜 깨우침을 줄 수 있습니다.즉, 실제로 조건이 시그널링되었는지 확인할 수 있도록 술어를 확인해야 하며, 이를 스플리어스 웨이크업과 구별해야 합니다.이러한 상태를 체크하는 것은 주의할 필요가 있습니다.따라서 조건 변수는 해당 상태를 보호하는 뮤텍스를 잠금/잠금 해제하는 동안 원자적으로 대기/웨이크업하는 방법이 필요합니다.

일부 데이터가 생성되었다는 통지를 받은 간단한 예를 생각해 보십시오.다른 스레드가 원하는 데이터를 만들고 해당 데이터에 포인터를 설정했을 수 있습니다.

생산자 스레드가 '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);
}

자연히 많은 레이스 조건을 갖게 될 것이다, 만약 다른 실이 그렇게 된다면?some_data = new_data네가 깨어난 직후에, 하지만 네가 깨기 전에data = some_data

이 케이스를 보호하기 위해 독자적인 뮤텍스를 작성할 수도 없습니다.

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);
}

중에 에 뮤텍스를 배치하는 것은이 되지 않습니다는 뮤텍스를 를 배치하는 것은 도움이 되지 않습니다.즉, mutex를 기다리는 동안 mutex를 잡을 수 없습니다.(이 경우 두 번째 조건 변수를 생성하여 작업이 끝났음을 알릴 수 있습니다.)some_data제작자를 로 하는 .- 「 」, 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」를 참조해 주세요.

따라서 대기/웨이크업 시 뮤텍스를 원자적으로 해제/그랩하는 방법이 필요합니다.이것이 pthread 조건 변수의 동작이며, 다음과 같이 합니다.

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);
}

(프로듀서는 당연히 같은 주의사항을 취할 필요가 있습니다.항상 같은 뮤텍스로 'some_data'를 보호하며, some_data가 현재 != NULL인 경우 some_data를 덮어쓰지 않도록 합니다.)

다른 답변은 이 페이지만큼 간결하고 읽기 쉽다고 생각하지 않습니다.일반적으로 대기 코드는 다음과 같습니다.

mutex.lock()
while(!check())
    condition.wait(mutex) # atomically unlocks mutex and sleeps. Calls 
                          # mutex.lock() once the thread wakes up.
mutex.unlock()

싸는 는 세 수 있습니다.wait()★★★★★★★★★★★★★★★★★★:

  1. 이 될 수 있다signal()before wait()깨어나는 걸 놓치게 될 거야
  2. 은 「」입니다.check()는 다른 스레드의 수정에 의존하므로 상호 제외해야 합니다.
  3. priority가 가장 높은 스레드가 먼저 진행되도록 합니다(mutex 큐를 통해 스케줄러는 다음 사용자를 결정할 수 있습니다).

세 번째 포인트는 항상 우려되는 것은 아닙니다.역사적 맥락은 기사부터 이 대화까지 연결되어 있습니다.

은 이 메커니즘에 는 대기 스레드가 ).signal() 이러한 는 루프된 상태됩니다.check().

POSIX 조건 변수는 스테이트리스입니다.그래서 국가를 유지하는 것은 당신의 책임입니다.상태는 대기하는 스레드와 다른 스레드에 대기 중지를 지시하는 스레드 모두에서 액세스되므로 뮤텍스로 보호해야 합니다.뮤텍스 없이 조건 변수를 사용할 수 있다고 생각되면 조건 변수가 상태 비저장임을 파악하지 못한 것입니다.

조건 변수는 조건을 중심으로 구축됩니다.조건 변수에서 대기하는 스레드는 어떤 조건을 기다리고 있습니다.조건 변수에 신호를 보내는 스레드는 해당 조건을 변경합니다.예를 들어, 스레드가 일부 데이터가 도착하기를 기다리고 있을 수 있습니다.데이터가 도착한 것을 다른 스레드가 알아차릴 수 있습니다."데이터가 도착했습니다"가 조건입니다.

조건 변수의 일반적인 용도는 다음과 같습니다.

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
}

스레드의 작업 대기 상태를 확인합니다.그 작품은 뮤텍스로 보호되고 있다.wait는 뮤텍스를 해제하여 다른 스레드가 이 스레드에 작업을 제공할 수 있도록 합니다.신호는 다음과 같습니다.

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);
}

작업 큐를 보호하려면 뮤텍스가 필요합니다.조건 변수 자체는 작업이 있는지 여부를 알 수 없습니다.즉, 조건 변수는 조건과 관련되어 있어야 하며, 해당 조건은 코드로 유지되어야 하며, 스레드 간에 공유되므로 뮤텍스로 보호해야 합니다.

모든 조건 변수 함수에 뮤텍스가 필요한 것은 아닙니다.대기 연산만 뮤텍스를 필요로 합니다.신호 및 브로드캐스트 동작에는 뮤텍스가 필요하지 않습니다.또한 조건 변수는 특정 뮤텍스와 영속적으로 관련지을 수 없습니다.외부 뮤텍스는 조건 변수를 보호하지 않습니다.조건 변수가 대기 스레드 큐와 같은 내부 상태를 가진 경우 조건 변수 내부의 내부 잠금으로 보호해야 합니다.

wait 조작은 다음과 같은 이유로 조건 변수와 뮤텍스를 결합합니다.

  • 스레드가 mutex를 잠그고 공유 변수에 대한 일부 식을 평가한 후 false로 판명되었기 때문에 대기해야 합니다.
  • 스레드는 mutex 소유에서 조건 대기로 원자적으로 이동해야 합니다.

따라서 wait 조작은 mutex와 condition을 모두 인수로서 사용합니다.따라서 mutex 소유에서 대기까지의 스레드의 원자 전송을 관리할 수 있기 때문에 스레드가 잃어버린 웨이크업 레이스 조건의 대상이 되지 않습니다.

스레드가 뮤텍스를 포기하고 스테이트리스 동기화 오브젝트에서 대기하는 경우 웨이크업 레이스 손실 상태가 발생합니다.단, 아토믹이 아닌 방법으로 스레드에 잠금이 설정되어 있지 않고 아직 오브젝트 대기 상태가 시작되지 않은 시간대가 존재합니다.이 창에서는 다른 스레드가 들어와 대기 상태를 true로 만들고 스테이트리스 동기화를 신호로 보낸 후 사라질 수 있습니다.상태 비저장 개체는 신호된 것을 기억하지 못합니다(상태 비저장).따라서 원래 스레드는 상태 비저장 동기화 개체에서 sleeve 상태가 되고 필요한 조건이 이미 충족되어도 웨이크업 상태가 되지 않습니다.

조건 변수 대기 함수는 발신 스레드가 뮤텍스를 포기하기 전에 확실하게 웨이크업을 캡처하도록 등록되어 있는지 확인함으로써 웨이크업 손실을 방지합니다.조건 변수 wait 함수가 mutex를 인수로 받아들이지 않으면 이 작업은 불가능합니다.

그것에 대해서는 많은 설명이 있지만, 나는 예를 들어 다음과 같이 요약하고 싶다.

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 }

코드 조각이 뭐가 문제죠?진행하기 전에 좀 더 숙고해 보세요.


그 문제는 정말 미묘하다.가 「」를 기동했을 .thr_parent()ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴdone 되어 있는 을 알 수 0자려고 노력해요.하지만 잠자기 전에 부모가 6-7행 사이에 끼어들어 아이가 달린다.합니다.done로로 합니다.1신호를 보내지만 대기 중인 스레드가 없으므로 스레드가 깨지지 않습니다.이치하다 정말 말도 안 되는 일이다.

획득된 잠금 장치를 개별적으로 실행하는 경우에는 어떻게 해야 합니까?

이는 개념적 요구라기보다는 구체적인 설계 결정으로 보입니다.

pthreads docs에 따르면 mutex가 분리되지 않은 이유는 mutex를 조합함으로써 퍼포먼스가 크게 향상되기 때문입니다.또한 mutex를 사용하지 않으면 일반적인 레이스 조건 때문에 거의 항상 mutex를 사용할 수 있을 것으로 예상하고 있습니다.

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

뮤텍스 및 조건 변수의 기능

뮤텍스의 취득과 해제를 조건 대기에서 분리하는 것이 제안되었습니다.이는 실제로 실시간 구현을 용이하게 하는 운영의 결합 특성이기 때문에 거부되었습니다.이러한 실장에서는 발신자에게 투과적인 방법으로 조건 변수와 뮤텍스 간에 고우선도 스레드를 아토믹하게 이동할 수 있습니다.이것에 의해, 여분의 콘텍스트스위치를 방지해, 대기 스레드가 시그널링 되었을 때에 뮤텍스를 취득할 수 있습니다.따라서 공정성과 우선순위 문제는 스케줄링 규율에 의해 직접 처리될 수 있습니다.또, 현재의 상태 대기 동작은, 기존의 프랙티스와 일치한다.

조건 변수는 뮤텍스와 관련지어집니다.왜냐하면 뮤텍스가 회피하도록 설계된 레이스를 회피할 수 있는 유일한 방법이기 때문입니다.

// 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!

이 시점에서는 조건 변수를 나타내는 스레드가 없으므로 protectedReadyToRunVariable이 준비되었다고 해도 thread1은 영원히 대기합니다.

이를 회피하는 유일한 방법은 조건변수가 뮤텍스를 원자적으로 해제함과 동시에 조건변수를 대기시키는 것입니다.이것이 cond_wait 함수에 뮤텍스가 필요한 이유입니다.

// 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);

condition variable의 실제 예를 원하시면 수업시간에 연습을 해 두었습니다.

#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);
}

뮤텍스는 당신이 전화할 때 잠겨 있어야 한다.pthread_cond_wait; atomically라고 부르면 mutex가 잠금 해제된 후 해당 조건으로 차단됩니다.조건이 시그널링되면 다시 atomic하게 잠긴 후 돌아갑니다.

이것에 의해, 필요에 따라서 예측 가능한 스케줄링을 실장할 수 있습니다.이것은 시그널링을 실행하는 스레드가 뮤텍스가 해방될 때까지 대기하고 나서, 그 처리를 실시해, 그 상태를 시그널링 할 수 있기 때문입니다.

언급URL : https://stackoverflow.com/questions/2763714/why-do-pthreads-condition-variable-functions-require-a-mutex

반응형