So erhalten Sie eine ganzzahlige Thread-ID in C ++ 11

83

c ++ 11 hat die Möglichkeit, die aktuelle Thread-ID abzurufen, kann jedoch nicht in einen ganzzahligen Typ umgewandelt werden:

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

Ausgabe: 139918771783456

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

Fehler: Ungültige Umwandlung vom Typ 'std :: thread :: id' in Typ 'uint64_t'. Gleiches gilt für andere Typen. Ungültige Umwandlung vom Typ 'std :: thread :: id' in Typ 'uint32_t'.

Ich möchte wirklich kein Zeiger-Casting durchführen, um die Ganzzahl-Thread-ID zu erhalten. Gibt es eine vernünftige Möglichkeit (Standard, weil ich möchte, dass es portabel ist), dies zu tun?

NoSenseEtAl
quelle
13
Wofür brauchst du eine ganze Zahl? Es ist garantiert nicht sinnvoll, Arithmetik jeglicher Art durchzuführen, und es ist außerhalb des Kontextes des Prozesses nicht sinnvoll. Daher sollte es nicht erforderlich sein, es zu serialisieren, außer für das Debuggen (was operator<<anscheinend in Ordnung ist).
Hmakholm verließ Monica
3
so etwas wie das: 1024cores.net/home/lock-free-algorithms/false-sharing---false, aber anstelle von N = MAX_THREAD_COUNT werde ich so etwas wie N = 128 haben und thread_id% N
NoSenseEtAl
9
Wenn Sie wirklich möchten, dass es portabel ist, müssen Sie auf die Möglichkeit vorbereitet sein, die überhaupt thread::idnicht als Ganzzahl dargestellt wird. Die Seite, auf die Sie verlinken, verwendet ein Array, das nach Thread-ID indiziert ist. Haben Sie darüber nachgedacht, map<thread::id, int>stattdessen ein zu verwenden? Anschließend können Sie die bereits für die idKlasse definierten Vergleichsoperatoren verwenden, ohne Konvertierungen vorzunehmen. Der Standard definiert auch hash<thread::id>, so dass Sie auch die ungeordneten Container verwenden können.
Rob Kennedy
2
@ Rob diese Karte würde Mutexing erfordern :(
NoSenseEtAl
1
@SwissFrank oder sollte ich CHF sagen: PI gibt es immer noch, aber ich denke, die akzeptierte Antwort ist in Ordnung für mich. Es liegt an mir, sicherzustellen, dass variable ID-Werte für die Dauer eines Programms eindeutig sind.
NoSenseEtAl

Antworten:

33

Die tragbare Lösung besteht darin, Ihre eigenen generierten IDs an den Thread zu übergeben.

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

Der std::thread::idTyp darf nur für Vergleiche verwendet werden, nicht für Arithmetik (dh wie auf der Dose steht: ein Bezeichner ). Sogar die von dargestellte Textdarstellung operator<<ist nicht spezifiziert , sodass Sie sich nicht darauf verlassen können, dass es sich um die Darstellung einer Zahl handelt.

Sie können auch eine Zuordnung von std::thread::idWerten zu Ihrer eigenen ID verwenden und diese Zuordnung (mit ordnungsgemäßer Synchronisierung) für die Threads freigeben, anstatt die ID direkt zu übergeben.

R. Martinho Fernandes
quelle
Aha! Aber es gibt eine Textdarstellung! Das ist gut genug für Menschen, um visuell einen Unterschied zwischen ihnen zu finden, oder?
Xunie
Die hier erwähnte Lösung thread :: id (oder this_thread :: get_id ()) ist am besten, da sie nicht programmiererspezifisch ist. In Mikes Stringstream-Antwort unten finden Sie eine Zeichenfolge- oder Ganzzahldarstellung.
Andrew
@ Andrew Ich habe das in der Antwort angesprochen: "Sogar die vom Operator << erzeugte Textdarstellung ist nicht spezifiziert, sodass Sie sich nicht darauf verlassen können, dass es sich um die Darstellung einer Zahl handelt." Es scheint, als ob eine zwielichtige Definition des Wortes "am besten" vorliegt.
R. Martinho Fernandes
"best" war nicht in Bezug auf die Zeichenfolgendarstellung.
Andrew
1
Auch ich habe gerade eine Benchmark mit 10.000.000 Iterationen um meinetwillen und this_thread :: GET_ID () ist böse schnell: pastebin.com/eLa3rKQE Debug - Modus nimmt ,0000002543827 Sekunden pro Anruf und Veröffentlichung nimmt ,00000003652367 Sekunden pro Anruf für mich. (Intel i5 2,60 GHz)
Andrew
82

Sie müssen nur tun

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

um eine zu bekommen size_t.

Aus der Referenz :

Die Vorlagenspezialisierung std::hashfür die std::thread::idKlasse ermöglicht es Benutzern, Hashes der Bezeichner von Threads abzurufen.

888
quelle
35
Ich denke das muss sein std::hash<std::thread::id>()(std::this_thread::get_id()), nicht wahr?
Barry
12
Wäre der Hash garantiert einzigartig? Wahrscheinlich nicht, da die Verwendung als eindeutige Thread-ID nicht mehr möglich ist.
Michael Goldshteyn
2
Das angegebene Beispiel funktioniert nicht mit mindestens Clang 3.4 und libstdc ++ 4.8. Die Neuformulierung von Barry funktioniert jedoch.
Arto Bendiken
3
danke 888 für die antwort. Der MS-Compiler hat thread :: id :: hash (), aber Barrys Code ist standardkonform. Hashes können kollidieren. Es ist noch nützlich, einen Hash pro Thread zu haben (mit hoffentlich einer Kollisionswahrscheinlichkeit nahe 0)
a.lasram
1
MSVC gibt in diesem Fall tatsächlich eine Hash- Thread-ID zurück. Sie können genauso gut Ihre eigenen ...
Rustyx
24

Eine andere ID (Idee? ^^) wäre die Verwendung von Stringstreams:

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

Und verwenden Sie try catch, wenn Sie keine Ausnahme wünschen, falls etwas schief geht ...

Mike
quelle
1
Gute Antwort. Dies würde im Allgemeinen dem Zweck dienen.
Iammilind
5
Dies ist nicht portierbar, da nicht garantiert werden kann, dass ein std::thread::idZeichen als Zeichen gedruckt wird, aus denen eine Ganzzahl besteht, ähnlich wie nicht garantiert wird, dass die Thread-ID intern durch eine Ganzzahl dargestellt wird.
Blubberdiblub
@blubberdiblub Wann wäre a std::thread::idkeine ganze Zahl?
Nikos
1
@Nikos, wenn eine Implementierung feststellt, dass eine Ganzzahl nicht ausreicht. Oder wann immer es aus einem anderen Grund als unangemessen erachtet wird. Der Punkt hier ist, dass Sie sich nicht darauf verlassen können und sollten, dass die Spezifikation in keiner Implementierung eine Ganzzahl ist, wenn die Spezifikation sie nicht als Ganzzahl angibt (und dies nicht, sie hat nur einige abstraktere Garantien). Verwenden Sie einfach std::thread::idals Typ anstelle einer Ganzzahl, dafür gibt es sie. Und interpretieren Sie die Zeichenfolgendarstellung nicht als Ziffern, aus denen eine Zahl besteht. Behandeln Sie es als undurchsichtig oder als Debugging- / Protokollierungsausgabe.
Blubberdiblub
6

Eine Idee wäre, den lokalen Thread-Speicher zum Speichern einer Variablen zu verwenden - egal welcher Typ, solange er den Regeln des lokalen Thread-Speichers entspricht - und dann die Adresse dieser Variablen als "Thread-ID" zu verwenden. Offensichtlich wird jede Arithmetik nicht sinnvoll sein, aber es wird ein integraler Typ sein.

Für die Nachwelt: Gibt pthread_self()a zurück pid_tund ist posix. Dies ist portabel für einige Definitionen von portabel.

gettid(), mit ziemlicher Sicherheit nicht tragbar, aber es gibt einen GDB-freundlichen Wert zurück.

tgoodhart
quelle
pthread_self()gibt tatsächlich a zurück pthread_t, was undurchsichtig ist (im Gegensatz zu pid_t(zurückgegeben von gettid()), das zwar auch plattformspezifisch ist, aber anscheinend zumindest eine ganze Zahl ist). Aber +1 für das erste bisschen, es hat mein Problem gelöst!
Cameron
4

Ich weiß wirklich nicht, wie schnell das geht, aber das ist die Lösung, die ich zu Gast bekommen habe:

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;

Wieder fange ich an zu denken, dass es die Antwort ist, einen Zeiger auf die Struktur zu bekommen und ihn in unsigned int oder uint64_t umzuwandeln ... EDIT:

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, um höllische Probleme zu vermeiden :) Das Umschreiben ist im Vergleich zur Suche nach solchen Fehlern einfach. :) :)

NoSenseEtAl
quelle
3
Sie haben keine Garantie dafür, dass Sie mit der hashFunktion keine doppelten Werte erhalten, geschweige denn , wenn Sie%% .
R. Martinho Fernandes
1
Sie können diese Garantie nicht mit bekommen std::this_thread::get_id()! Aber du brauchst es wahrscheinlich nicht. Ein paar Threads, die miteinander geteilt werden, verursachen nicht das gleiche massive Problem wie jeder Thread, der mit jedem anderen Thread geteilt wird. So etwas const size_t N_COUNTERS = 128; struct Counter { std::atomic<int> counter; char pad[CACHE_LINE_SIZE - sizeof(atomic<int>); } counters[N_COUNTERS];ist wahrscheinlich in Ordnung. (Ein Atomic oder Spinlock für eine sehr leichte Synchronisation.)
Scott Lamb
@R. Martinho Fernandes Wie ich schon sagte, ich interessiere mich für int value, damit ich es% kann. Kollisionen sind in Ordnung, wenn sie selten sind, im Grunde das, was Scott gesagt hat.
NoSenseEtAl
1
Ich habe es tatsächlich versucht und mich völlig geirrt - nur zu verwenden atomic<int>statt intist eine dramatische Verlangsamung, auch ohne Streit.
Scott Lamb
1
Sie können den static_assert durch etwas wie dieses ersetzen : ideone.com/Q7Nh4 (leicht zu ändern, um eine genaue Größenanforderung zu erzwingen, wenn Sie dies stattdessen möchten), damit es portabler funktioniert (beachten Sie, dass ideone beispielsweise eine 32-Bit-Thread-ID hat). .
R. Martinho Fernandes
4

thread::native_handle()gibt zurück thread::native_handle_type, was ein typedef ist long unsigned int.

Wenn der Thread standardmäßig erstellt wird, gibt native_handle () 0 zurück. Wenn ein Betriebssystem-Thread angeschlossen ist, ist der Rückgabewert ungleich Null (unter POSIX ist er pthread_t).

Alexey Polonsky
quelle
Wo ist angegeben, für std::thread::native_handle_typewas ein typedef ist long unsigned? In 30.3.1 / 1 können wir nur sehentypedef implementation-defined native_handle_type; // See 30.2.3
Ruslan
Eine dumme, aber einfache Möglichkeit, den Typ zu ermitteln, besteht darin, einen absichtlichen Kompilierungsfehler zu generieren, indem Sie beispielsweise uint8_t thread :: native_handle () zuweisen. Dann beschwert sich der Compiler über Typinkongruenzen und teilt Ihnen auch mit, um welchen Typ es sich handelt.
Alexey Polonsky
1
Nun, das ist nicht portabel, da es auf einer bestimmten Implementierung beruht.
Ruslan
Nun, zumindest wenn die zugrunde liegende Implementierung POSIX pthread verwendet, scheint native_handle () ein pthread_t zu sein. Jetzt ist pthread_t ein Zeigertyp (typedef struct pthread * pthread_t). Daher ist es sinnvoll, dass std :: thread :: native_handle_type ein ganzzahliger Typ ist, der einen Zeiger enthalten kann (z. B. size_t oder unsigned long).
Alexey Polonsky
3

Auf diese Weise sollte funktionieren:

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

Denken Sie daran, Library Sstream einzuschließen

Federico Rizzo
quelle
Schön, aber warum nimmst du an, dass es eine ganze Zahl ist? Es kann hex oder irgendetwas anderes sein.
Rustyx
Wenn Sie verwenden std::stringstream, können Sie es verwenden operator >>, um in int zu konvertieren. Ich würde eigentlich lieber uint64_tals Art idstatt , intwenn ich bin sicher , dass das idIntegral ist.
Aniliitb10
2

es hängt davon ab, wofür Sie die thread_id verwenden möchten; Sie können verwenden:

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

Dadurch wird während des Prozesses eine eindeutige ID generiert. Es gibt jedoch eine Einschränkung: Wenn Sie mehrere Instanzen desselben Prozesses starten und jede von ihnen ihre Thread-IDs in eine gemeinsame Datei schreibt, kann die Eindeutigkeit der thread_id nicht garantiert werden. In der Tat ist es sehr wahrscheinlich, dass Sie Überschneidungen haben. In diesem Fall können Sie Folgendes tun:

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

Jetzt sind Ihnen systemweit eindeutige Thread-IDs garantiert.

Pandrei
quelle
Der Überladene operator<<kann alles drucken , es ist falsch anzunehmen, dass er immer eine ganze Zahl druckt.
Rustyx
2

Ein Hauptgrund, thread :: get_id () nicht zu verwenden, ist, dass es in einem einzelnen Programm / Prozess nicht eindeutig ist. Dies liegt daran, dass die ID nach Abschluss des ersten Threads für einen zweiten Thread wiederverwendet werden kann.

Dies scheint eine schreckliche Funktion zu sein, aber es ist was in C ++ 11.

midjji
quelle
2

Eine andere Alternative:

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

Der von g ++ in x86 64-Bit generierte Code für diese Funktion lautet nur:

_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

Dh ein einzelner Zweig ohne Synchronisation, der korrekt vorhergesagt wird, außer wenn Sie die Funktion zum ersten Mal aufrufen. Danach nur noch ein einziger Speicherzugriff ohne Synchronisation.

6502
quelle
@NoSenseEtAl: Ich bin mir nicht sicher, ob ich Ihre Frage verstehe. thread_localBeschreibt bereits die Speicherdauer für tid. Das staticfür thread_counterist, weil Sie es nicht außerhalb dieser Kompilierungseinheit verfügbar machen möchten.
6502
Diese Art der seltsamen Zuweisung von Thread-IDs in der Reihenfolge, in der Sie die Thread-ID abfragen. (Ich habe selbst etwas SEHR Ähnliches gemacht, und ich habe diese Verrücktheit nie gemocht.) Es wird auch von Null zugewiesen, was nicht üblich ist. (Zum Beispiel meldet GDB Thread-IDs ab 1.)
Swiss Frank
1
@SwissFrank: Es ist nur eine Zahl und Sie sollten nicht zu viel in dem zurückgegebenen Wert lesen: Es gibt keinen legalen Weg zu wissen, dass er zugewiesen wurde, als Sie ihn abgefragt haben :-). Über die Tatsache, dass 0es sich um eine gültige ID handelt, ist dies ein guter Punkt und kann stattdessen mithilfe von Vorinkrementen behoben werden. Ich werde die Antwort ändern, um das zu tun.
6502
1

Vielleicht ist diese Lösung für jemanden hilfreich. Nenne es ein erstes Mal im main(). Warnung: nameswächst auf unbestimmte Zeit.

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];
}
geh
quelle
Verwenden Sie keinen Stringstream, er ist langsam. Verwenden Sie std :: to_string
NoSenseEtAl