Programing

C ++ 11에서 thread_local은 무엇을 의미합니까?

lottogame 2020. 8. 13. 07:38
반응형

C ++ 11에서 thread_local은 무엇을 의미합니까?


thread_localC ++ 11 의 설명과 혼동됩니다 . 내 이해는 각 스레드가 함수에 고유 한 로컬 변수 복사본을 가지고 있다는 것입니다. 전역 / 정적 변수는 모든 스레드에서 액세스 할 수 있습니다 (잠금을 사용하여 동기화 된 액세스 가능). 그리고 thread_local변수는 모든 스레드에 표시되지만 정의 된 스레드에 의해서만 수정할 수 있습니까? 맞습니까?


스레드-로컬 저장 기간은 (사용하는 함수의 관점에서) 겉보기에 글로벌 또는 정적 저장 기간 인 데이터를 가리키는 데 사용되는 용어이지만 실제로는 스레드 당 하나의 사본이 있습니다.

현재 자동 (블록 / 함수 동안 존재), 정적 (프로그램 기간 동안 존재) 및 동적 (할당과 할당 해제 사이의 힙에 존재)에 추가됩니다.

스레드 로컬 인 것은 스레드 생성시 존재하게되며 스레드가 중지되면 폐기됩니다.

다음은 몇 가지 예입니다.

시드가 스레드 단위로 유지되어야하는 난수 생성기를 생각해보십시오. 스레드 로컬 시드를 사용한다는 것은 각 스레드가 다른 스레드와 독립적으로 자체 난수 시퀀스를 가져옴을 의미합니다.

시드가 랜덤 함수 내의 지역 변수 인 경우 호출 할 때마다 초기화되어 매번 동일한 숫자를 제공합니다. 글로벌 인 경우 스레드는 서로의 시퀀스를 방해합니다.

또 다른 예는 strtok토큰 화 상태가 스레드 별 기준으로 저장되는 것과 같습니다 . 이렇게하면 단일 스레드가 다른 스레드가 토큰 화 노력을 망치지 않도록 할 수 있으며 동시에 여러 호출을 통해 상태를 유지할 수 있습니다. strtok이것은 기본적으로 strtok_r(스레드 안전 버전) 중복을 렌더링합니다 .

이 두 예제 모두 스레드 로컬 변수 가이를 사용하는 함수 내에 존재하도록 허용 합니다. 미리 스레드 된 코드에서는 단순히 함수 내의 정적 저장 기간 변수 일 것입니다. 스레드의 경우 스레드 로컬 저장 기간으로 수정됩니다.

또 다른 예는 errno. errno호출 중 하나가 실패한 후 변수를 확인하기 전에 별도의 스레드가 수정되는 것을 원하지 않지만 스레드 당 하나의 복사본 만 필요합니다.

이 사이트 에는 다양한 저장 기간 지정자에 대한 합리적인 설명 이 있습니다 .


변수 thread_local선언하면 각 스레드에는 자체 복사본이 있습니다. 이름으로 참조하면 현재 스레드와 연관된 사본이 사용됩니다. 예 :

thread_local int i=0;

void f(int newval){
    i=newval;
}

void g(){
    std::cout<<i;
}

void threadfunc(int id){
    f(id);
    ++i;
    g();
}

int main(){
    i=9;
    std::thread t1(threadfunc,1);
    std::thread t2(threadfunc,2);
    std::thread t3(threadfunc,3);

    t1.join();
    t2.join();
    t3.join();
    std::cout<<i<<std::endl;
}

이 코드는 "2349", "3249", "4239", "4329", "2439"또는 "3429"를 출력하지만 다른 것은 출력하지 않습니다. 각 스레드에는 i에 할당되고 증가 된 다음 인쇄되는 자체 복사본이 있습니다. 실행중인 스레드 main에는 자체 복사본이 있으며 처음에 할당 된 다음 변경되지 않은 상태로 유지됩니다. 이러한 사본은 완전히 독립적이며 각각 다른 주소를 갖습니다.

그 점에서 특별한 이름 일뿐입니다. --- thread_local변수 의 주소를 취하면 스레드간에 자유롭게 전달할 수있는 일반 객체에 대한 일반 포인터 만 갖게됩니다. 예 :

thread_local int i=0;

void thread_func(int*p){
    *p=42;
}

int main(){
    i=9;
    std::thread t(thread_func,&i);
    t.join();
    std::cout<<i<<std::endl;
}

Since the address of i is passed to the thread function, then the copy of i belonging to the main thread can be assigned to even though it is thread_local. This program will thus output "42". If you do this, then you need to take care that *p is not accessed after the thread it belongs to has exited, otherwise you get a dangling pointer and undefined behaviour just like any other case where the pointed-to object is destroyed.

thread_local variables are initialized "before first use", so if they are never touched by a given thread then they are not necessarily ever initialized. This is to allow compilers to avoid constructing every thread_local variable in the program for a thread that is entirely self-contained and doesn't touch any of them. e.g.

struct my_class{
    my_class(){
        std::cout<<"hello";
    }
    ~my_class(){
        std::cout<<"goodbye";
    }
};

void f(){
    thread_local my_class unused;
}

void do_nothing(){}

int main(){
    std::thread t1(do_nothing);
    t1.join();
}

In this program there are 2 threads: the main thread and the manually-created thread. Neither thread calls f, so the thread_local object is never used. It is therefore unspecified whether the compiler will construct 0, 1 or 2 instances of my_class, and the output may be "", "hellohellogoodbyegoodbye" or "hellogoodbye".


Thread-local storage is in every aspect like static (= global) storage, only that each thread has a separate copy of the object. The object's life time starts either at thread start (for global variables) or at first initialization (for block-local statics), and ends when the thread ends (i.e. when join() is called).

Consequently, only variables that could also be declared static may be declared as thread_local, i.e. global variables (more precisely: variables "at namespace scope"), static class members, and block-static variables (in which case static is implied).

As an example, suppose you have a thread pool and want to know how well your work load was being balanced:

thread_local Counter c;

void do_work()
{
    c.increment();
    // ...
}

int main()
{
    std::thread t(do_work);   // your thread-pool would go here
    t.join();
}

This would print thread usage statistics, e.g. with an implementation like this:

struct Counter
{
     unsigned int c = 0;
     void increment() { ++c; }
     ~Counter()
     {
         std::cout << "Thread #" << std::this_thread::id() << " was called "
                   << c << " times" << std::endl;
     }
};

참고URL : https://stackoverflow.com/questions/11983875/what-does-the-thread-local-mean-in-c11

반응형