Jak uzyskać identyfikator wątku w postaci liczby całkowitej w języku C ++ 11


85

c ++ 11 ma możliwość pobrania bieżącego id wątku, ale nie można go rzutować na typ całkowity:

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

wyjście: 139918771783456

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

błąd: nieprawidłowe rzutowanie z typu „std :: thread :: id” do typu „uint64_t” to samo dla innych typów: nieprawidłowe rzutowanie z typu „std :: thread :: id” do typu „uint32_t”

Naprawdę nie chcę rzucać wskaźników, aby uzyskać identyfikator wątku będący liczbą całkowitą. Czy jest jakiś rozsądny sposób (standardowy, ponieważ chcę, aby był przenośny), aby to zrobić?


13
Do czego ma to być liczba całkowita? Gwarantuje się, że nie ma sensu wykonywać na nim żadnych działań arytmetycznych i nie ma to sensu poza kontekstem procesu, więc nie powinno być potrzeby serializacji tego innego niż debugowanie (które operator<<wydaje się dobrze obsługiwać).
hmakholm zostawił Monikę

4
coś takiego: 1024cores.net/home/lock-free-algorithms/false-sharing---false ale zamiast N = MAX_THREAD_COUNT będę miał coś takiego jak N = 128 i zrobię thread_id% N
NoSenseEtAl

9
Jeśli naprawdę chcesz, aby był przenośny, musisz być przygotowany na możliwość, która w thread::idogóle nie jest reprezentowana jako liczba całkowita. Strona, do której prowadzi łącze, korzysta z tablicy indeksowanej według identyfikatora wątku. Czy rozważałeś użycie map<thread::id, int>zamiast tego? Następnie możesz użyć operatorów relacyjnych już zdefiniowanych dla idklasy bez wykonywania żadnych konwersji. Norma również definiuje hash<thread::id>, więc możesz używać również nieuporządkowanych kontenerów.
Rob Kennedy,

3
@Rob, ta mapa wymagałaby mutexingu :(
NoSenseEtAl

1
@SwissFrank lub powinienem powiedzieć CHF: PI wciąż jestem w pobliżu, ale myślę, że zaakceptowana odpowiedź jest dla mnie w porządku, to do mnie należy upewnienie się, że wartości zmiennych id są unikalne na czas trwania programu.
NoSenseEtAl

Odpowiedzi:


34

Przenośnym rozwiązaniem jest przekazanie własnych wygenerowanych identyfikatorów do wątku.

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

std::thread::idTyp ma być wykorzystywane do porównań, nie dla arytmetyki (czyli jak jest napisane na puszce: AN identyfikatora ). Nawet jego reprezentacja tekstowa utworzona przez operator<<jest nieokreślona , więc nie można polegać na tym, że jest reprezentacją liczby.

Możesz także użyć mapy std::thread::idwartości na swój własny identyfikator i udostępnić tę mapę (z odpowiednią synchronizacją) między wątkami, zamiast przekazywać identyfikator bezpośrednio.


1
Aha! Ale jest reprezentacja tekstowa! To wystarczy, aby ludzie mogli wizualnie odróżnić je, prawda?
Xunie

Wspomniane tutaj rozwiązanie thread :: id (lub this_thread :: get_id ()) jest najlepsze, ponieważ nie jest specyficzne dla programisty. Zobacz odpowiedź Mike'a na stringstream poniżej, aby uzyskać reprezentację w postaci ciągu lub liczby całkowitej.
Andrew,

@Andrew Odniosłem się do tego w odpowiedzi: „Nawet jego reprezentacja tekstowa utworzona przez operator << jest nieokreślona, ​​więc nie można polegać na tym, że jest reprezentacją liczby”. Wydaje się, że pod ręką jest podejrzana definicja słowa „najlepszy”.
R. Martinho Fernandes

„best” nie było w odniesieniu do reprezentacji ciągu.
Andrew,

1
Poza tym właśnie wykonałem test porównawczy z 10000000 iteracji dla własnego dobra, a this_thread :: get_id () działa szybko: pastebin.com/eLa3rKQE Tryb debugowania zajmuje 0,0000002543827 sekund na połączenie, a Release zajmuje 0,00000003652367 sekund na wywołanie. (Intel i5 2,60 GHz)
Andrew,

85

Po prostu musisz to zrobić

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

aby dostać size_t.

Od cppreference :

Specjalizacja szablonów std::hashdla std::thread::idklasy umożliwia użytkownikom uzyskiwanie skrótów identyfikatorów wątków.


35
Myślę, że tak musi być std::hash<std::thread::id>()(std::this_thread::get_id()), prawda?
Barry

12
Czy gwarantowany byłby niepowtarzalny hash? Prawdopodobnie nie, pokonując jego użycie jako unikalnego identyfikatora wątku.
Michael Goldshteyn

2
Podany przykład nie działa z co najmniej Clang 3.4 i libstdc ++ 4.8. Jednak zmiana sformułowania Barry'ego działa.
Arto Bendiken

3
dzięki 888 za odpowiedź. Kompilator MS ma funkcję thread :: id :: hash (), ale kod Barry'ego jest zgodny ze standardami. Hashe mogą się zderzać. Warto jeszcze mieć hash na wątek (miejmy nadzieję, że prawdopodobieństwo kolizji jest bliskie 0)
a.lasram

1
W tym przypadku MSVC faktycznie zwraca hashowany identyfikator wątku.
Równie

25

Innym id (pomysł? ^^) byłoby użycie stringstreams:

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

I użyj try catch, jeśli nie chcesz mieć wyjątku na wypadek, gdyby coś poszło nie tak ...


2
Dobra odpowiedź. Spełniłoby to ogólne zadanie.
iammilind

5
Nie jest to przenośne, ponieważ nie ma gwarancji, że znak zostanie std::thread::idwydrukowany jako znaki składające się na liczbę całkowitą w taki sam sposób, w jaki nie ma gwarancji, że identyfikator wątku jest wewnętrznie reprezentowany przez liczbę całkowitą.
blubberdiblub

1
@Nikos zawsze, gdy implementacja uzna, że ​​liczba całkowita jest niewystarczająca. Lub gdy uzna to za niewłaściwe z jakiegokolwiek innego powodu. Chodzi o to, że jeśli specyfikacja nie określa jej jako liczby całkowitej (a tak nie jest, ma po prostu bardziej abstrakcyjne gwarancje), nie możesz i nie powinieneś polegać na tym, że jest to liczba całkowita w żadnej implementacji. Po prostu użyj std::thread::idjako typu zamiast jakiejś liczby całkowitej, po to istnieje. I nie interpretuj ponownie jego reprezentacji w postaci łańcucha jako cyfr tworzących liczbę. Traktuj je jako nieprzezroczyste lub jako wyjście debugowania / rejestrowania.
blubberdiblub

6

Jednym z pomysłów byłoby użycie lokalnego magazynu wątków do przechowywania zmiennej - bez względu na typ, o ile jest on zgodny z zasadami lokalnego przechowywania wątków - a następnie użycie adresu tej zmiennej jako „identyfikatora wątku”. Oczywiście żaden arytemetyka nie będzie znaczący, ale będzie typem integralnym.

Dla potomnych: pthread_self()zwraca a pid_ti jest posix. To jest przenośne dla niektórych definicji przenośnych.

gettid(), prawie na pewno nie przenośny, ale zwraca wartość przyjazną dla GDB.


pthread_self()w rzeczywistości zwraca wartość pthread_t, która jest nieprzezroczysta (w przeciwieństwie do pid_t(zwracana przez gettid()), która, chociaż jest również specyficzna dla platformy, jest najwyraźniej przynajmniej liczbą całkowitą). Ale +1 za pierwszy bit, to rozwiązało mój problem!
Cameron

4

Naprawdę nie wiem, jak szybko to jest, ale jest to rozwiązanie, które udało mi się gościć:

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;

Znowu zaczynam myśleć, że uzyskanie wskaźnika do struktury i rzutowanie go na unsigned int lub uint64_t jest odpowiedzią ... EDYCJA:

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, aby zapobiec piekielnym problemom :) Przepisywanie jest łatwe w porównaniu do polowania na tego rodzaju błędy. :)


3
Nie masz gwarancji, że nie otrzymasz zduplikowanych wartości z hashfunkcją, a tym bardziej, jeśli% ją% .
R. Martinho Fernandes

1
Nie możesz dostać tej gwarancji z std::this_thread::get_id()! Ale prawdopodobnie tego nie potrzebujesz. Udostępnianie kilku wątków nie stanowi tego samego ogromnego problemu, co każde udostępnianie wątków w każdym innym wątku. Coś jak const size_t N_COUNTERS = 128; struct Counter { std::atomic<int> counter; char pad[CACHE_LINE_SIZE - sizeof(atomic<int>); } counters[N_COUNTERS];jest prawdopodobnie w porządku. (Atomic lub spinlock do bardzo lekkiej synchronizacji.)
Scott Lamb,

@R. Martinho Fernandes Jak powiedziałem, interesuje mnie wartość int, więc mogę ją%, kolizje są w porządku, jeśli są rzadkie, w zasadzie to, co powiedział Scott.
NoSenseEtAl

1
Naprawdę próbowałem tego i całkowicie się myliłem - samo użycie atomic<int>zamiast intto dramatyczne spowolnienie, nawet bez rywalizacji.
Scott Lamb,

1
Możesz zastąpić static_assert czymś takim jak ten ideone.com/Q7Nh4 (łatwo modyfikowalny, aby wymusić dokładny rozmiar, jeśli chcesz zamiast tego), aby działał bardziej przenośnie (zwróć uwagę, jak ideone ma na przykład 32-bitowy identyfikator wątku) .
R. Martinho Fernandes

4

thread::native_handle()zwraca thread::native_handle_type, co jest typedef to long unsigned int.

Jeśli wątek jest zbudowany domyślnie, native_handle () zwraca 0. Jeśli jest do niego dołączony wątek systemu operacyjnego, wartość zwracana jest różna od zera (jest to pthread_t w POSIX).


Gdzie określono, że std::thread::native_handle_typejest to typedef long unsigned? W 30.3.1 / 1 możemy zobaczyć tylkotypedef implementation-defined native_handle_type; // See 30.2.3
Ruslan

Głupim, ale prostym sposobem na odkrycie typu jest celowe wygenerowanie błędu kompilacji poprzez przypisanie funkcji thread :: native_handle () do np. Uint8_t. Wtedy kompilator narzeknie na niezgodność typów, a także powie Ci, jaki to typ.
Alexey Polonsky

1
Cóż, to nieprzenośne, ponieważ opiera się na konkretnej implementacji.
Ruslan

Cóż, przynajmniej jeśli podstawowa implementacja używa POSIX pthread, wydaje się, że native_handle () musi być pthread_t. Teraz pthread_t jest typem wskaźnikowym (typedef struct pthread * pthread_t). Dlatego ma sens, że std :: thread :: native_handle_type jest typem całkowitym, który może zawierać wskaźnik (np. Size_t lub unsigned long).
Alexey Polonsky

3

W ten sposób powinno działać:

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

Pamiętaj o dołączeniu strumienia biblioteki


Fajnie, ale dlaczego zakładasz, że jest to liczba całkowita? Może to być hex lub cokolwiek innego.
rustyx

jeśli używasz std::stringstream, możesz użyć jego operator >>do konwersji na int. Właściwie wolałbym uint64_tjako typ idzamiast tego, intczy jestem pewien, że idjest całka.
aniliitb10

3

Głównym powodem, dla którego nie należy używać funkcji thread :: get_id () jest to, że nie jest ona unikalna w pojedynczym programie / procesie. Dzieje się tak, ponieważ identyfikator może zostać ponownie użyty dla drugiego wątku, po zakończeniu pierwszego wątku.

Wydaje się, że to okropna funkcja, ale to, co jest w C ++ 11.


2

zależy to od tego, do czego chcesz użyć thread_id; możesz użyć:

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

Spowoduje to wygenerowanie unikalnego identyfikatora podczas przetwarzania; ale jest ograniczenie: jeśli uruchamiasz kilka instancji tego samego procesu i każdy z nich zapisuje swoje identyfikatory wątków do wspólnego pliku, unikalność thread_id nie jest gwarantowana; w rzeczywistości jest bardzo prawdopodobne, że będziesz się pokrywać. W takim przypadku możesz zrobić coś takiego:

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

teraz masz zagwarantowane unikalne identyfikatory wątków w całym systemie.


Przeciążony operator<<może wydrukować wszystko , błędem jest zakładać, że zawsze wypisze liczbę całkowitą.
rustyx

2

Inna alternatywa:

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

Wygenerowany kod dla tej funkcji przez g ++ w 64-bitowej architekturze x86 to po prostu:

_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

To znaczy pojedyncza gałąź bez synchronizacji, która będzie poprawnie przewidziana, z wyjątkiem pierwszego wywołania funkcji. Potem tylko jeden dostęp do pamięci bez synchronizacji.


@NoSenseEtAl: Nie jestem pewien, czy rozumiem Twoje pytanie ... thread_localjuż opisuje czas przechowywania tid. staticZa thread_counterto, bo nie chce narażać go na zewnątrz tej jednostki kompilacji.
6502

Ten rodzaj dziwnie przypisuje identyfikatory wątków w kolejności, w jakiej pytasz o identyfikator wątku. (Sam zrobiłem coś BARDZO podobnego i nigdy nie podobało mi się to dziwne.) Również przypisuje od zera, co nie jest normalne. (Na przykład GDB zgłasza identyfikatory wątków zaczynające się od 1.)
Swiss Frank

1
@SwissFrank: to tylko liczba i nie powinieneś czytać zbyt dużo w zwracanej wartości: nie ma legalnego sposobu, aby dowiedzieć się, że została przypisana podczas zapytania :-). O tym, że 0jest to ważny identyfikator, który jest dobrym punktem i można go zamiast tego naprawić za pomocą preinkrementu. Zmienię odpowiedź, żeby to zrobić.
6502

1

Może to rozwiązanie komuś pomoże. Nazwij to po raz pierwszy im main(). Ostrzeżenie: namesrośnie w nieskończoność.

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

nie używaj stringstream, jest powolny, użyj std ::
to_string
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.