수색…


소개

C 및 C ++에서 가장 어려운 작업 중 하나는 리소스 관리입니다. 고맙게도 C ++에서는 프로그램에서 자원 관리를 설계 할 수있는 많은 방법이 있습니다. 이 기사에서는 할당 된 리소스를 관리하는 데 사용되는 숙어 및 방법에 대해 설명하고자합니다.

자원 획득 초기화

RAII (Resource Acquisition Is Initialization)는 리소스 관리에서 일반적인 관용구입니다. 동적 메모리의 경우 스마트 포인터 를 사용하여 리소스 관리를 수행합니다. RAII를 사용할 때, 획득 된 자원은 즉시 스마트 포인터 또는 동등한 자원 관리자에게 소유권을 부여받습니다. 리소스는이 관리자를 통해서만 액세스 할 수 있으므로 관리자는 다양한 작업을 추적 할 수 있습니다. 예를 들어, std::auto_ptr 은 범위를 벗어나거나 삭제 될 때 자동으로 해당 자원을 해제합니다.

#include <memory>
#include <iostream>
using namespace std;

int main() {
    {
        auto_ptr ap(new int(5)); // dynamic memory is the resource
        cout << *ap << endl; // prints 5
    } // auto_ptr is destroyed, its resource is automatically freed
}
C ++ 11

std::auto_ptr 의 주된 문제는 소유권을 이전하지 않고 복사 할 수 없다는 것입니다.

#include <memory>
#include <iostream>
using namespace std;

int main() {
    auto_ptr ap1(new int(5));
    cout << *ap1 << endl; // prints 5
    auto_ptr ap2(ap1); // copy ap2 from ap1; ownership now transfers to ap2
    cout << *ap2 << endl; // prints 5
    cout << ap1 == nullptr << endl; // prints 1; ap1 has lost ownership of resource
}

이 이상한 복사 의미론 때문에, std::auto_ptr 은 컨테이너에서 사용할 수 없습니다. 이렇게하는 이유는 메모리를 두 번 삭제하는 것을 방지하기 위해서입니다. 같은 리소스의 소유권을 가진 auto_ptrs 가 두 개인 경우, 둘 다 파괴됩니다. 이미 해제 된 리소스를 해제하면 일반적으로 문제가 발생할 수 있으므로이를 방지하는 것이 중요합니다. 그러나 std::shared_ptr 에는 복사를 피할 때 소유권을 이전하지 않고이를 피하는 방법이 있습니다.

#include <memory>
#include <iostream>
using namespace std;

int main() {
    shared_ptr sp2;
    {
        shared_ptr sp1(new int(5)); // give ownership to sp1
        cout << *sp1 << endl; // prints 5
        sp2 = sp1; // copy sp2 from sp1; both have ownership of resource
        cout << *sp1 << endl; // prints 5
        cout << *sp2 << endl; // prints 5
    } // sp1 goes out of scope and is destroyed; sp2 has sole ownership of resource
    cout << *sp2 << endl;        
} // sp2 goes out of scope; nothing has ownership, so resource is freed

뮤텍스 및 스레드 안전성

여러 스레드가 자원에 액세스하려고 할 때 문제가 발생할 수 있습니다. 간단한 예를 들어, 변수에 스레드를 추가하는 스레드가 있다고 가정 해보십시오. 먼저 변수를 읽고, 변수를 추가 한 다음 다시 저장합니다. 이 변수를 1로 초기화 한 다음이 스레드의 두 인스턴스를 생성한다고 가정합니다. 두 스레드가 모두 끝나면 직관력은이 변수의 값이 3이어야 함을 나타냅니다. 그러나 아래 표는 무엇이 잘못 될 수 있는지 보여줍니다.

글타래 1 주제 2
시간 단계 1 변수에서 1을 읽습니다.
시간 단계 2 변수에서 1을 읽습니다.
시간 단계 3 1을 더하면 2가됩니다.
시간 단계 4 1을 더하면 2가됩니다.
시간 단계 5 변수 2에 저장
시간 단계 6 변수 2에 저장

볼 수 있듯이, 작업이 끝날 때 2는 3 대신 변수에 있습니다. 이유는 스레드 2가 스레드 1이 스레드를 업데이트하기 전에 변수를 읽었기 때문입니다. 해결책? 뮤텍스.

뮤텍스 (MUT 연간 전직 clusion의 합성어)는 이러한 유형의 문제를 해결하기 위해 설계된 자원 관리 객체입니다. 스레드가 자원에 액세스하려고하면 스레드는 자원의 뮤텍스를 "획득"합니다. 리소스에 대한 액세스가 완료되면 스레드는 뮤텍스를 "릴리스"합니다. 뮤텍스가 획득되는 동안 뮤텍스를 획득하기위한 모든 호출은 뮤텍스가 해제 될 때까지 반환되지 않습니다. 이를 더 잘 이해하려면 슈퍼마켓에서 뮤텍스를 대기 행렬로 생각하십시오. 스레드는 뮤텍스를 획득하려고 시도하고 그 다음 스레드가 끝나기를 기다린 다음 자원을 사용하고 뮤텍스를 해제하여 라인. 모든 사람들이 한 번에 자원에 액세스하려고하면 완전한 판돈이 될 것입니다.

C ++ 11

std::mutex 는 C ++ 11에서 뮤텍스를 구현 한 것입니다.

#include <thread>
#include <mutex>
#include <iostream>
using namespace std;

void add_1(int& i, const mutex& m) { // function to be run in thread
    m.lock();
    i += 1;
    m.unlock();
}

int main() {
    int var = 1;
    mutex m;

    cout << var << endl; // prints 1
    
    thread t1(add_1, var, m); // create thread with arguments
    thread t2(add_1, var, m); // create another thread
    t1.join(); t2.join(); // wait for both threads to finish
    
    cout << var << endl; // prints 3
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow