C ++ 11에서 정수 스레드 ID를 얻는 방법


84

c ++ 11은 현재 스레드 ID를 가져올 수 있지만 정수 유형으로 캐스팅 할 수 없습니다.

cout<<std::this_thread::get_id()<<endl;

출력 : 139918771783456

cout<<(uint64_t)std::this_thread::get_id()<<endl;

오류 : 'std :: thread :: id'유형에서 다른 유형에 대해 동일한 'uint64_t'유형으로의 잘못된 캐스트 : 'std :: thread :: id'유형에서 'uint32_t'유형으로의 잘못된 캐스트

정수 스레드 ID를 얻기 위해 포인터 캐스팅을 정말로 원하지 않습니다. 그것을하기위한 합리적인 방법 (휴대용이되기를 원하기 때문에 표준)이 있습니까?


13
정수가 되려면 무엇이 필요합니까? 어떤 종류의 산술도 수행하는 것은 의미가 없으며 프로세스의 컨텍스트 밖에서는 의미가 없으므로 디버깅 ( operator<<잘 처리 하는 것처럼 보임) 외에는 직렬화 할 필요가 없습니다 .
hmakholm 모니카 남은

4
이 같은 : 1024cores.net/home/lock-free-algorithms/false-sharing---false 대신 N = MAX_THREAD_COUNT I의는 N = 128 같은 것을 가지고 thread_id % N 할 것
NoSenseEtAl

9
정말 이식 가능 thread::id하게하려면 정수로 표시되지 않는 가능성에 대비해야 합니다. 링크하는 페이지는 스레드 ID로 색인 된 배열을 사용합니다. map<thread::id, int>대신 사용을 고려해 보셨습니까 ? 그런 다음 id변환을 수행하지 않고 클래스에 대해 이미 정의 된 관계 연산자를 사용할 수 있습니다 . 표준은 또한 정의 hash<thread::id>하므로 정렬되지 않은 컨테이너도 사용할 수 있습니다.
Rob Kennedy

3
@Rob 그 맵은 뮤텍스가 필요합니다 :(
NoSenseEtAl

1
@SwissFrank 또는 CHF : PI라고 말해야합니까?하지만 받아 들인 대답은 나에게 괜찮다고 생각합니다. 변수 ID 값이 프로그램 기간 동안 고유한지 확인하는 것은 나에게 달려 있습니다.
NoSenseEtAl

답변:


33

이식 가능한 솔루션은 자신이 생성 한 ID를 스레드에 전달하는 것입니다.

int id = 0;
for(auto& work_item : all_work) {
    std::async(std::launch::async, [id,&work_item]{ work_item(id); });
    ++id;
}

std::thread::id유형은 비교에 사용되는 경우에만,하지 (즉,이 캔에 말한대로 : 산술에 대한 식별자 ). 생산에도 해당 텍스트 표현 operator<<입니다 지정되지 않은 당신이 숫자의 표현 인에 의존하지 수 있습니다.

또한 std::thread::id자신의 ID에 대한 값 맵을 사용 하고 ID를 직접 전달하는 대신 스레드간에 적절한 동기화를 통해이 맵을 공유 할 수 있습니다.


1
아하! 그러나이 있다 텍스트 표현! 인간이 시각적으로 구별하기에는 충분합니다.
Xunie

여기에 언급 된 thread :: id (또는 this_thread :: get_id ()) 솔루션은 프로그래머에 한정되지 않기 때문에 가장 좋습니다. 문자열 또는 정수 표현을 얻으려면 아래 Mike의 stringstream 답변을 참조하십시오.
Andrew

@Andrew 나는 대답에서 "연산자 <<에 의해 생성 된 텍스트 표현조차도 지정되지 않았으므로 숫자의 표현이라고 믿을 수 없습니다"라고 답했습니다. "최고"라는 단어의 그늘진 정의가 가까이있는 것 같습니다.
R. Martinho Fernandes

"best"는 문자열 표현과 관련이 없습니다.
Andrew

1
또한, 난 그냥 10,000,000 내 자신을 위해 반복과 함께 벤치 마크를했다 this_thread :: get_id () 사악한 빠른입니다 : pastebin.com/eLa3rKQE 디버그 모드는 호출 당 0.0000002543827 초 소요 릴리스 나를 위해 호출 당 0.00000003652367 초 정도 걸립니다. (인텔은 2.60 GHz의를 I5)
앤드류

85

당신은 할 필요가 있습니다

std::hash<std::thread::id>{}(std::this_thread::get_id())

를 얻을 수 있습니다 size_t.

에서 cppreference :

클래스 에 std::hash대한 템플릿 전문화를 std::thread::id통해 사용자는 스레드 식별자의 해시를 얻을 수 있습니다.


35
나는 이것이이어야한다고 생각한다 std::hash<std::thread::id>()(std::this_thread::get_id()), 그렇지?
Barry

12
해시가 고유하게 보장됩니까? 아마도 그렇지 않을 것입니다. 고유 한 스레드 식별자로서의 사용을 무효화합니다.
Michael Goldshteyn 2013 년

2
주어진 예제는 Clang 3.4 및 libstdc ++ 4.8 이상에서 작동하지 않습니다. 그러나 Barry의 재구성은 효과가 있습니다.
Arto Bendiken 2014 년

3
답을 주셔서 감사합니다 888. MS 컴파일러에는 thread :: id :: hash ()가 있지만 Barry의 코드는 표준을 준수합니다. 해시가 충돌 할 수 있습니다. 스레드 당 해시를 갖는 것은 아직 유용합니다 (충돌 확률이 0에
가까움

1
이 경우 MSVC는 실제로 해시 된 스레드 ID를 반환합니다 . 당신은 당신의 자신의 ... 생성 할 수 있습니다
rustyx

25

또 다른 ID (아이디어? ^^)는 stringstreams를 사용하는 것입니다.

std::stringstream ss;
ss << std::this_thread::get_id();
uint64_t id = std::stoull(ss.str());

일이 잘못되는 경우 예외를 원하지 않으면 try catch를 사용하십시오.


2
좋은 대답입니다. 이것은 일반적으로 목적에 부합합니다.
iammilind

5
std::thread::id스레드 ID가 내부적으로 정수로 표현된다는 보장이없는 것과 거의 동일한 방식으로 정수를 구성하는 문자 로 인쇄 된다는 보장이 없기 때문에 이식성이 없습니다 .
blubberdiblub

1
@Nikos는 구현에서 정수가 불충분하다고 선택할 때마다. 또는 다른 이유로 부적절하다고 판단 될 때마다. 여기서 요점은 사양이 정수로 지정하지 않을 때 (그렇지 않고 더 많은 추상적 인 보증이 있음) 어떤 구현에서도 정수로 의존 할 수 없으며 의존해서는 안된다는 것입니다. std::thread::id정수 대신 유형으로 사용하기 만하면 됩니다. 그리고 문자열 표현을 숫자를 구성하는 숫자로 재 해석하지 마십시오. 불투명하거나 디버깅 / 로깅 출력으로 처리하십시오.
blubberdiblub

6

한 가지 아이디어는 변수를 저장하기 위해 스레드 로컬 저장소를 사용하는 것입니다. 스레드 로컬 저장소의 규칙을 준수하는 한 어떤 유형이든 상관없이 해당 변수의 주소를 "스레드 ID"로 사용하는 것입니다. 분명히 arithemetic은 의미가 없지만 통합 유형이 될 것입니다.

후손의 경우 : pthread_self()a를 반환하고 pid_tposix입니다. 이것은 휴대용의 일부 정의를 위해 이식 가능합니다.

gettid(), 거의 확실하게 이식 할 수는 없지만 GDB 친화적 인 값을 반환합니다.


pthread_self()실제로 pthread_t는 불투명 한를 pid_t반환합니다 (에서 반환 됨 gettid()). 이는 플랫폼에 따라 다르지만 최소한 정수인 것 같습니다. 그러나 첫 번째 비트는 +1, 그것은 내 문제를 해결했습니다!
Cameron

4

나는 이것이 얼마나 빠른지 정말로 모른다. 그러나 이것이 내가 게스트화할 수있는 해결책이다.

const size_t N_MUTEXES=128;//UINT_MAX,not 128  for answer to my original question
hash<std::thread::id> h;
cout<<h(std::this_thread::get_id())%N_MUTEXES<<endl;

다시 나는 구조에 대한 포인터를 얻고 unsigned int 또는 uint64_t로 캐스팅하는 것이 답이라고 생각하기 시작했습니다 ... 편집 :

uint64_t get_thread_id()
{
    static_assert(sizeof(std::thread::id)==sizeof(uint64_t),"this function only works if size of thead::id is equal to the size of uint_64");
    auto id=std::this_thread::get_id();
    uint64_t* ptr=(uint64_t*) &id;
    return (*ptr);
}
int main()
{
    cout<<std::this_thread::get_id()<<"  "<<get_thread_id()<<endl;
}

지옥 같은 문제를 방지하기위한 static_assert :) 재 작성은 이런 종류의 버그를 찾는 것에 비해 쉽습니다. :)


3
hash함수로 중복 값을 얻지 않을 것이라는 보장은 없으며 % it이면 훨씬 적습니다 .
R. Martinho Fernandes

1
당신은 그 보증을 얻을 수 없습니다 std::this_thread::get_id()! 그러나 아마도 필요하지 않을 것입니다. 서로 공유하는 몇 개의 스레드는 모든 스레드가 다른 모든 스레드와 공유하는 것과 동일한 큰 문제를 일으키지 않습니다. 같은 const size_t N_COUNTERS = 128; struct Counter { std::atomic<int> counter; char pad[CACHE_LINE_SIZE - sizeof(atomic<int>); } counters[N_COUNTERS];것은 아마 괜찮습니다. (매우 가벼운 동기화를위한 원자 또는 스핀 락.)
Scott Lamb

@아르 자형. Martinho Fernandes 내가 int 값에 관심이 있다고 말했듯이, 내가 할 수 있도록 할 수 있습니다. 충돌이 드물더라도 괜찮습니다. 기본적으로 Scott이 말한 것입니다.
NoSenseEtAl 2011 년

1
나는 실제로 이것을 시도했고 완전히 틀렸다. atomic<int>대신 사용 하는 int것은 경합 없이도 극적인 속도 저하이다.
Scott Lamb

1
static_assert를 다음과 같은 ideone.com/Q7Nh4 (대신 원하는 경우 정확한 크기 요구 사항을 적용하기 위해 쉽게 조정할 수 있음)로 대체하여 더 이식 가능하게 할 수 있습니다 (예를 들어 ideone에 32 비트 스레드 ID가있는 방법에 유의하십시오). .
R. Martinho Fernandes

4

thread::native_handle()thread::native_handle_type대한 typedef 인을 반환 합니다 long unsigned int.

thread가 기본 생성 된 경우 native_handle ()은 0을 반환합니다. 연결된 OS 스레드가있는 경우 반환 값은 0이 아닙니다 (POSIX의 pthread_t).


std::thread::native_handle_typetypedef는 어디에 지정 되어 long unsigned있습니까? 30.3.1 / 1에서 우리는 볼 수 있습니다typedef implementation-defined native_handle_type; // See 30.2.3
Ruslan

유형을 발견하는 멍청하지만 간단한 방법은 예를 들어 uint8_t에 thread :: native_handle ()을 할당하여 의도적 인 컴파일 오류를 생성하는 것입니다. 그런 다음 컴파일러는 유형 불일치에 대해 불평하고 유형이 무엇인지 알려줄 것입니다.
Alexey Polonsky

1
특정 구현에 의존하기 때문에 이식이 불가능합니다.
Ruslan

적어도 기본 구현이 POSIX pthread를 사용하는 경우 native_handle ()은 pthread_t 여야합니다. 이제 pthread_t는 포인터 유형입니다 (typedef struct pthread * pthread_t). 따라서 std :: thread :: native_handle_type은 포인터를 포함 할 수있는 정수 유형입니다 (예 : size_t 또는 unsigned long).
Alexey Polonsky

3

이런 식으로 작동해야합니다.

std::stringstream ss;
ss << std::this_thread::get_id();
int id = std::stoi(ss.str());

라이브러리 sstream을 포함하는 것을 잊지 마십시오


좋지만 왜 정수라고 가정합니까? 16 진수 또는 다른 것이 될 수 있습니다.
rustyx

당신이 사용하는 경우 std::stringstream, 당신은 사용할 수 있습니다 operator >>INT로 변환 할 수 있습니다. 나는 그것이 적분 이라고 확신하는 대신에 실제로 uint64_t유형으로 선호합니다 . idintid
aniliitb10

3

thread :: get_id ()를 사용하지 않는 주요 이유는 단일 프로그램 / 프로세스에서 고유하지 않기 때문입니다. 첫 번째 스레드가 완료되면 두 번째 스레드에 ID를 재사용 할 수 있기 때문입니다.

이것은 끔찍한 기능처럼 보이지만 C ++ 11에있는 것입니다.


2

그것은 당신이 thread_id를 사용하고자하는 것에 달려 있습니다; 당신이 사용할 수있는:

std::stringstream ss;
ss << std::this_thread::get_id();
uint64_t id = std::stoull(ss.str());

이것은 당신이 처리하는 고유 ID를 생성합니다. 그러나 제한이 있습니다. 동일한 프로세스의 여러 인스턴스를 시작하고 각 인스턴스가 스레드 ID를 공통 파일에 기록하면 thread_id의 고유성이 보장되지 않습니다. 실제로 중복 될 가능성이 매우 높습니다. 이 경우 다음과 같이 할 수 있습니다.

#include <sys/time.h>
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
uint64_t id = (ts.tv_sec % 1000000000) * 1000000000 + ts.tv_nsec;

이제 시스템 전체에 고유 한 스레드 ID가 보장됩니다.


오버로드 operator<<무엇이든 인쇄 할 수 있습니다 . 항상 정수를 인쇄한다고 가정하는 것은 잘못되었습니다.
rustyx

2

또 다른 대안 :

#include <atomic>

static std::atomic<unsigned long long> thread_counter;

unsigned long long thread_id() {
    thread_local unsigned long long tid = ++thread_counter;
    return tid;
}

x86 64 비트에서 g ++에 의해이 함수에 대해 생성 된 코드는 다음과 같습니다.

_Z9thread_idv:
        cmp     BYTE PTR fs:_ZGVZ9thread_idvE3tid@tpoff, 0
        je      .L2
        mov     rax, QWORD PTR fs:_ZZ9thread_idvE3tid@tpoff
        ret
.L2:
        mov     eax, 1
        lock xadd       QWORD PTR _ZL14thread_counter[rip], rax
        mov     BYTE PTR fs:_ZGVZ9thread_idvE3tid@tpoff, 1
        mov     QWORD PTR fs:_ZZ9thread_idvE3tid@tpoff, rax
        ret
_ZGVZ9thread_idvE3tid:
        .zero   8
_ZZ9thread_idvE3tid:
        .zero   8

즉, 함수를 처음 호출 할 때를 제외하고 올바르게 예측되는 동기화가없는 단일 분기입니다. 그 후 동기화없이 단일 메모리 액세스 만 가능합니다.


@NoSenseEtAl : 질문을 이해하지 못했습니다 ... thread_local이미에 대한 저장 기간에 대해 설명합니다 tid. static에는 thread_counter이 컴파일 단위 외부에 노출하고 싶지 않기 때문이다.
6502

이런 종류의 이상한 스레드 ID를 쿼리하는 순서대로 스레드 ID를 할당합니다. (나는 나 자신과 매우 비슷한 것을 해왔고, 나는이 이상한 점을 결코 좋아하지 않았다.) 그것은 또한 일반적이지 않은 0에서 할당한다. (예를 들어 GDB는 1부터 시작하는 스레드 ID를보고합니다.)
Swiss Frank

1
@SwissFrank : 숫자 일 뿐이며 반환 된 값을 너무 많이 읽어서는 안됩니다. 쿼리 할 때 할당되었음을 알 수있는 법적 방법은 없습니다. :-). 0좋은 점이며 대신 사전 증가를 사용하여 수정할 수있는 유효한 ID 라는 사실에 대해 . 그렇게하려면 대답을 변경하겠습니다.
6502

1

이 솔루션이 누군가에게 도움이 될 수 있습니다. 그것을 처음으로 불러라 main(). 경고 : names무기한 증가합니다.

std::string currentThreadName(){
    static std::unordered_map<std::thread::id,std::string> names;
    static std::mutex mtx;

    std::unique_lock<std::mutex> lock(mtx);

    auto id = std::this_thread::get_id();

    if(names.empty()){
        names[id] = "Thread-main";
    } else if(names.find(id) == names.end()){
        std::stringstream stream;
        stream << "Thread-" << names.size();
        names[id] = stream.str();
    }

    return names[id];
}

이제 stringstream을 사용하지 않는, 그것은 천천히, 사용 표준 : to_string
NoSenseEtAl
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.