itsource

뮤텍스 예/자습서?

mycopycode 2022. 8. 29. 22:22
반응형

뮤텍스 예/자습서?

멀티스레딩은 처음이라서 뮤텍스가 어떻게 작동하는지 이해하려고 했습니다.구글링을 많이 했지만 잠금이 작동하지 않는 프로그램을 직접 만들었기 때문에 여전히 어떻게 작동하는지 의문이 남았습니다.

구문 중 하나는 뮤뮤의 of of of of of of of of of of of of of of of of of of of of of of of of of of of of of이다.pthread_mutex_lock( &mutex1 ); mutex가 잠겨 있는 것처럼 보이는 경우, 내가 정말로 잠그고 싶은 것은 다른 변수입니다.이 구문은 뮤텍스를 잠그면 뮤텍스가 잠금 해제될 때까지 코드 영역이 잠긴다는 의미입니까?그럼 스레드는 지역이 잠겨 있는 것을 어떻게 알 수 있을까요?[업데이트: 스레드는 메모리 펜싱을 통해 해당 영역이 잠겨 있음을 인식합니다.그리고 그런 현상을 임계 구간이라고 해야 하지 않나요?[UPDATE: 중요한 섹션 오브젝트는 Windows에서만 사용할 수 있습니다.이 경우 오브젝트는 뮤텍스보다 고속으로 구현된 스레드에만 표시됩니다. 그렇지 않으면 critical 섹션은 mutex에 의해 보호되는 코드 영역만 참조합니다.

간단히 말해서, 가능한 가장 간단한 mutex 예제 프로그램과 작동 원리에 대한 가장 간단한 설명을 도와주시겠습니까?나는 이것이 다른 많은 신입사원들에게 도움이 될 것이라고 확신한다.

세계 각지의 신입사원들에게 컨셉을 설명하려는 저의 겸손한 시도입니다. (내 블로그에도 컬러 코딩 버전이 있습니다.)

많은 사람들이 사랑하는 사람과 통화하기 위해 (휴대전화가 없는) 혼자 있는 공중전화 부스로 달려간다.부스의 문고리를 가장 먼저 잡는 사람은 전화를 사용할 수 있는 사람이다.그는 전화를 사용하는 한 문의 손잡이를 계속 잡고 있어야 한다.그렇지 않으면 다른 사람이 손잡이를 잡고 그를 내쫓고 아내와 대화할 것이다:) 그런 대기 행렬 시스템은 없다.통화를 마치고 부스에서 나와 도어 핸들을 놓고 나오면 다음에 도어 핸들을 잡은 사람이 전화를 사용할 수 있게 됩니다.

스레드는 다음과 같습니다.각자
mutex는 : 도어핸들
자물쇠는 : 그 사람의 손
리소스는 전화입니다.

다른 스레드에 의해 동시에 변경되어서는 안 되는 코드 행을 실행할 필요가 있는 스레드(부스의 도어 핸들을 닫는 것)는, 우선 뮤텍스의 잠금을 취득할 필요가 있습니다.그 후에, 스레드는 이러한 코드 행(전화 발신)을 실행할 수 있게 됩니다.

스레드가 그 코드를 실행한 후에는 뮤텍스 잠금을 해제하여 다른 스레드가 뮤텍스 잠금을 취득할 수 있도록 합니다(다른 사용자가 전화 박스에 액세스 할 수 있습니다).

[뮤텍스를 갖는다는 개념은 현실의 배타적 접근을 고려할 때 다소 터무니없지만, 프로그래밍 세계에서는 스레드가 이미 줄의 코드를 실행하고 있다는 것을 다른 스레드가 '보여줄' 수 있는 방법이 없었던같습니다. 재귀 뮤텍스 등의 개념이 있지만 이 예에서는 기본적인 개념을 보여 주기 위한 것입니다. 이 예에서 개념을 명확하게 알 수 있기를 바랍니다.]

C++11 스레드의 경우:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex m;//you can use std::lock_guard if you want to be exception safe
int i = 0;

void makeACallFromPhoneBooth() 
{
    m.lock();//man gets a hold of the phone booth door and locks it. The other men wait outside
      //man happily talks to his wife from now....
      std::cout << i << " Hello Wife" << std::endl;
      i++;//no other thread can access variable i until m.unlock() is called
      //...until now, with no interruption from other men
    m.unlock();//man lets go of the door handle and unlocks the door
}

int main() 
{
    //This is the main crowd of people uninterested in making a phone call

    //man1 leaves the crowd to go to the phone booth
    std::thread man1(makeACallFromPhoneBooth);
    //Although man2 appears to start second, there's a good chance he might
    //reach the phone booth before man1
    std::thread man2(makeACallFromPhoneBooth);
    //And hey, man3 also joined the race to the booth
    std::thread man3(makeACallFromPhoneBooth);

    man1.join();//man1 finished his phone call and joins the crowd
    man2.join();//man2 finished his phone call and joins the crowd
    man3.join();//man3 finished his phone call and joins the crowd
    return 0;
}

를 사용하여 컴파일하여 합니다.g++ -std=c++0x -pthread -o thread thread.cpp;./thread

으로 「」를 사용하는 것이 아니라lock ★★★★★★★★★★★★★★★★★」unlock스코프 잠금을 사용하면 다음같이 브래킷을 사용할 수 있습니다.단, 스코프 잠금에는 약간의 퍼포먼스 오버헤드가 있습니다.

뮤텍스는 다른 문제를 해결하기 위해 사용될 수 있지만, 그들이 존재하는 주된 이유는 상호 배제를 제공하고 그에 따라 인종 조건으로 알려진 것을 해결하기 위해서이다.두 개 이상의 스레드 또는 프로세스가 동일한 변수에 동시에 액세스하려고 하면 레이스 상태가 될 가능성이 있습니다.다음 코드를 고려합니다.

//somewhere long ago, we have i declared as int
void my_concurrently_called_function()
{
  i++;
}

이 함수의 내부는 매우 단순해 보입니다.진술이 하나일 뿐이야.단, 일반적인 유사 어셈블리 언어는 다음과 같습니다.

load i from memory into a register
add 1 to i
store i back into memory

i에 대한 증분 작업을 수행하려면 동등한 어셈블리 언어 명령이 모두 필요하기 때문에 i의 증분 작업은 비자동 작업이라고 합니다.원자 연산은 명령 실행이 시작되면 중단되지 않는다는 보장 하에 하드웨어 상에서 완료할 수 있는 연산이다.증가 i는 3개의 원자 명령 체인으로 구성됩니다.여러 스레드가 함수를 호출하는 동시 시스템에서는 스레드가 잘못된 시간에 읽거나 쓸 때 문제가 발생합니다.두 개의 스레드가 동시에 실행되고 한 스레드가 다른 스레드 바로 다음에 함수를 호출한다고 가정해 보십시오.또한 i가 0으로 초기화되었다고 합시다.또한 레지스터가 풍부하고 두 스레드가 완전히 다른 레지스터를 사용하므로 충돌이 발생하지 않는다고 가정합니다.이러한 이벤트의 실제 타이밍은 다음과 같습니다.

thread 1 load 0 into register from memory corresponding to i //register is currently 0
thread 1 add 1 to a register //register is now 1, but not memory is 0
thread 2 load 0 into register from memory corresponding to i
thread 2 add 1 to a register //register is now 1, but not memory is 0
thread 1 write register to memory //memory is now 1
thread 2 write register to memory //memory is now 1

두 개의 스레드가 동시에 증가하고 함수가 두 번 호출되지만 결과는 사실과 일치하지 않습니다.함수가 한 번만 호출된 것 같습니다.이는 기계 수준에서 원자성이 "끊겨져" 있기 때문입니다. 즉, 쓰레드가 서로 간섭하거나 잘못된 시간에 함께 작동할 수 있습니다.

우리는 이것을 해결하기 위한 메커니즘이 필요하다.위의 지시에 대해 몇 가지 주문을 부과할 필요가 있습니다.하나의 일반적인 메커니즘은 하나의 스레드를 제외한 모든 스레드를 차단하는 것입니다.Pthread mutex는 이 메커니즘을 사용합니다.

다른 스레드에 의해 공유 값이 안전하지 않게 동시에 변경될 수 있는 코드의 몇 줄을 실행해야 하는 스레드는 먼저 뮤텍스 잠금을 취득해야 합니다.이렇게 하면 공유 데이터에 액세스해야 하는 스레드는 모두 뮤텍스 잠금을 통과해야 합니다.그래야 스레드가 코드를 실행할 수 있습니다.코드의 이 섹션을 크리티컬 섹션이라고 합니다.

스레드는 크리티컬섹션을 실행한 후 뮤텍스의 잠금을 해제하여 다른 스레드가 뮤텍스의 잠금을 취득할 수 있도록 합니다.

뮤텍스를 갖는다는 개념은 실제 물리적인 물체에 대한 배타적 접근을 추구하는 인간을 고려할 때 약간 이상해 보이지만 프로그래밍을 할 때 우리는 의도적이어야 한다.동시 스레드 및 프로세스는 델과 같은 사회적, 문화적 교양이 없기 때문에 데이터를 적절히 공유하도록 강요해야 합니다.

엄밀히 말하면 뮤텍스는 어떻게 작동할까요?아까 말씀드린 것과 같은 레이스 조건에 시달리는 것은 아닙니까?pthread_mutex_lock()은 변수의 단순한 증가보다 조금 더 복잡하지 않습니까?

엄밀히 말하면, 도움이 되는 하드웨어 지원이 필요합니다.하드웨어 설계자들은 우리에게 하나 이상의 일을 하면서도 원자성을 보증하는 기계 지시를 내립니다.이러한 명령의 전형적인 예는 Test-and-Set(TAS; 테스트 앤 세트)입니다.리소스에 대한 잠금을 취득하려고 할 때 TAS를 사용하여 메모리 값이 0인지 여부를 확인할 수 있습니다.이 경우 리소스가 사용 중이고 아무것도 하지 않는다는 신호입니다(정확히 말하면 어떤 메커니즘에 의해 대기합니다).pthreads mutex는 운영체제의 특별한 큐에 들어가 리소스를 사용할 수 있게 되면 알려 줍니다.덤버 시스템에서는 엄격한 스핀 루프를 실행하여 상태를 반복해서 테스트해야 할 수 있습니다).메모리 값이 0이 아닌 경우 TAS는 다른 명령을 사용하지 않고 위치를 0 이외의 값으로 설정합니다.두 개의 조립 명령어를 1로 조합하여 원자성을 얻는 것과 같습니다.따라서 테스트 및 값 변경(변경이 적절한 경우)은 일단 시작되면 중단할 수 없습니다.이러한 명령어 위에 뮤텍스를 구축할 수 있습니다.

주의: 일부 섹션은 이전 답변과 유사할 수 있습니다.저는 편집 요청을 수락했고, 그는 원래 방식을 선호했습니다. 그래서 저는 그의 말투를 약간 주입한 제가 가진 것을 간직하고 있습니다.

shortex mutex의 예를 찾으시는 분:

#include <mutex>

int main() {
    std::mutex m;

    m.lock();
    // do thread-safe stuff
    m.unlock();
}

★★pthread_mutex_lock()는 발신측 스레드의 뮤텍스를 취득하거나 뮤텍스를 취득할 수 있을 때까지 스레드를 차단합니다.관련 정보pthread_mutex_unlock()을 사용하다

mutex를 큐로 간주합니다.mutex를 취득하려고 하는 모든 스레드는 큐의 끝에 배치됩니다.스레드가 뮤텍스를 해제하면 큐의 다음 스레드가 해제되어 실행됩니다.

크리티컬 섹션은 비결정론이 가능한 코드 영역을 말합니다.이는 여러 스레드가 공유 변수에 액세스하려고 하기 때문에 자주 발생합니다.중요한 섹션은 동기화가 이루어지지 않는 한 안전하지 않습니다.뮤텍스 잠금은 동기화의 한 형태입니다.

제가 아는 최고의 스레드 튜토리얼은 다음과 같습니다.

https://computing.llnl.gov/tutorials/pthreads/

특정 구현에 관한 것이 아니라 API에 대해 쓰여져 있어 동기화를 이해하는 데 도움이 되는 간단한 예를 몇 가지 제시해 주는 것이 마음에 듭니다.

얼마 전 우연히 이 게시물을 보고 표준 라이브러리의 c++11 mutex(std::mutex)용 최신 솔루션이 필요하다고 생각했습니다.

아래에 코드를 붙여넣었습니다(mutex를 사용한 첫 단계 - HANDLE, SetEvent, WaitForMultipleObjects 등이 있는 win32에서 동시성을 배웠습니다).

std::mutex 및 친구들과의 첫 시도이기 때문에, 코멘트, 제안, 개선점을 보고 싶습니다!

#include <condition_variable>
#include <mutex>
#include <algorithm>
#include <thread>
#include <queue>
#include <chrono>
#include <iostream>


int _tmain(int argc, _TCHAR* argv[])
{   
    // these vars are shared among the following threads
    std::queue<unsigned int>    nNumbers;

    std::mutex                  mtxQueue;
    std::condition_variable     cvQueue;
    bool                        m_bQueueLocked = false;

    std::mutex                  mtxQuit;
    std::condition_variable     cvQuit;
    bool                        m_bQuit = false;


    std::thread thrQuit(
        [&]()
        {
            using namespace std;            

            this_thread::sleep_for(chrono::seconds(5));

            // set event by setting the bool variable to true
            // then notifying via the condition variable
            m_bQuit = true;
            cvQuit.notify_all();
        }
    );


    std::thread thrProducer(
        [&]()
        {
            using namespace std;

            int nNum = 13;
            unique_lock<mutex> lock( mtxQuit );

            while ( ! m_bQuit )
            {
                while( cvQuit.wait_for( lock, chrono::milliseconds(75) ) == cv_status::timeout )
                {
                    nNum = nNum + 13 / 2;

                    unique_lock<mutex> qLock(mtxQueue);
                    cout << "Produced: " << nNum << "\n";
                    nNumbers.push( nNum );
                }
            }
        }   
    );

    std::thread thrConsumer(
        [&]()
        {
            using namespace std;
            unique_lock<mutex> lock(mtxQuit);

            while( cvQuit.wait_for(lock, chrono::milliseconds(150)) == cv_status::timeout )
            {
                unique_lock<mutex> qLock(mtxQueue);
                if( nNumbers.size() > 0 )
                {
                    cout << "Consumed: " << nNumbers.front() << "\n";
                    nNumbers.pop();
                }               
            }
        }
    );

    thrQuit.join();
    thrProducer.join();
    thrConsumer.join();

    return 0;
}

뮤텍스로 보호된 영역을 사용하기 전에 뮤텍스 변수를 확인해야 합니다.따라서 pthread_mutex_lock()은 (실장에 따라) mutex1이 해제될 때까지 기다리거나 다른 사용자가 이미 잠금을 잠근 경우 잠금을 가져올 수 없음을 나타내는 값을 반환합니다.

뮤텍스는 단순한 세마포어입니다.만약 당신이 그들에 대해 읽고 이해한다면, 당신은 뮤텍스를 이해하게 될 것입니다.SO의 뮤텍스와 세마포어에 관한 몇 가지 질문이 있습니다.바이너리 세마포와 뮤텍스차이, 뮤텍스를 사용할 때와 세마포어를 사용할 때의 차이 등이 있습니다.첫 번째 링크의 화장실 예는 생각할 수 있는 한 좋은 예입니다.코드가 하는 일은 키가 사용 가능한지 확인하고 사용 가능한 경우 예약하는 것입니다.화장실 자체를 예약하는 것이 아니라 열쇠라는 점에 유의하십시오.

SEMAPHORE의 예:

sem_t m;
sem_init(&m, 0, 0); // initialize semaphore to 0

sem_wait(&m);
// critical section here
sem_post(&m);

참조: http://pages.cs.wisc.edu/ ~remzi / Classes / 537 / Fall 2008 / Notes / threads - semaphores . txt

언급URL : https://stackoverflow.com/questions/4989451/mutex-example-tutorial

반응형