Cum să obțineți un id de fir întreg în c++11 (Programare, C++, Multithreading, C++11)

NoSenseEtAl a intrebat.

c++11 are o posibilitate de a obține id-ul firului curent, dar nu este castabil în tipul întreg:

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

ieșire : 13991818771783456

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

error: invalid cast from type ‘std::thread::id’ to type ‘uint64_t’același lucru pentru alte tipuri:invalid cast from type ‘std::thread::id’ to type ‘uint32_t’

Chiar nu vreau să fac turnarea pointerului pentru a obține id-ul de fir întreg. Există vreo modalitate rezonabilă (standard, deoarece vreau să fie portabilă) de a face acest lucru?

Comentarii

  • De ce aveți nevoie să fie un număr întreg? Este garantat că nu are sens să faci aritmetică de orice fel pe el și nu are sens în afara contextului procesului, deci nu ar trebui să fie nevoie să îl serializezi decât pentru depanare (ceea ce nu este necesar pentru operator<< pare să se descurce bine). –  > Por hmakholm a rămas peste Monica.
  • ceva de genul acesta:1024cores.net/home/lock-free-algorithms/false-sharing—false dar în loc de N=MAX_THREAD_COUNT voi avea ceva de genul N=128 și voi face thread_id%N –  > Por NoSenseEtAl.
  • Dacă doriți cu adevărat să fie portabil, atunci trebuie să fiți pregătiți pentru posibilitatea că thread::id nu este reprezentat deloc ca un număr întreg. Pagina la care faceți legătura folosește un array, indexat după ID-ul firului. V-ați gândit să folosiți un map<thread::id, int> în schimb? Atunci puteți utiliza operatorii relaționali deja definiți pentru id fără a face conversii. Standardul definește, de asemenea hash<thread::id>, , astfel încât să puteți utiliza și containerele neordonate. –  > Por Rob Kennedy.
  • @Rob acea hartă ar necesita mutexare 🙁 –  > Por NoSenseEtAl.
  • @SwissFrank sau ar trebui să spun CHF 😛 Sunt încă prin preajmă, dar cred că răspunsul acceptat este în regulă pentru mine, depinde de mine să mă asigur că valorile id ale variabilelor sunt unice pe durata unui program. –  > Por NoSenseEtAl.
11 răspunsuri
R. Martinho Fernandes

Soluția portabilă este de a trece propriile ID-uri generate în firul de execuție.

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

Adresa std::thread::id trebuie folosit doar pentru comparații, nu pentru aritmetică (adică, așa cum scrie pe cutie: un tip identificator). Chiar și reprezentarea sa textuală produsă de operator<< este nespecificată, , deci nu vă puteți baza pe faptul că este reprezentarea unui număr.

De asemenea, ați putea utiliza o hartă de std::thread::id valorilor la propriul id și să partajați această hartă (cu o sincronizare adecvată) între firele de execuție, în loc să transmiteți direct id-ul.

Comentarii

  • Aha! este o reprezentare text! Este suficient de bun pentru ca oamenii să găsească vizual distincția între ele, nu? –  > Por Xunie.
  • Soluția thread::id (sau this_thread::get_id())) menționată aici este cea mai bună, deoarece nu este specifică programatorului. Consultați răspunsul lui Mike la stringstream de mai jos pentru a obține o reprezentare sub formă de șir sau întreg. –  > Por Andrew.
  • @Andrew Am abordat acest aspect în răspuns: „Chiar și reprezentarea sa text produsă de operator<< este nespecificată, deci nu vă puteți baza pe faptul că este reprezentarea unui număr”. Se pare că avem de-a face cu o definiție dubioasă a cuvântului „cel mai bun”. –  > Por R. Martinho Fernandes.
  • „cel mai bun” nu era în legătură cu reprezentarea șirului de caractere. –  > Por Andrew.
  • De asemenea, tocmai am făcut un benchmark cu 10.000.000.000 de iterații de dragul meu și this_thread::get_id() este extrem de rapid: pastebin.com/eLa3rKQE Modul Debug durează 0,0000002543827 secunde pe apel, iar modul Release durează 0,00000003652367 secunde pe apel pentru mine. (Intel i5 2,60 GHz) –  > Por Andrew.
888

Trebuie doar să faceți

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

pentru a obține un size_t.

De la cppreference:

Specializarea șablonului de std::hash pentru std::thread::id permite utilizatorilor să obțină hașuri ale identificatorilor firelor de execuție.

Comentarii

    35

  • Cred că acest lucru trebuie să fie std::hash<std::thread::id>()(std::this_thread::get_id()), , nu-i așa? –  > Por Barry.
  • Ar fi garantat hash-ul unic? Probabil că nu, ceea ce ar împiedica utilizarea sa ca identificator unic al firului. –  > Por Michael Goldshteyn.
  • Exemplul dat nu funcționează cel puțin cu Clang 3.4 și libstdc++ 4.8. Cu toate acestea, reformularea lui Barry funcționează. –  > Por Arto Bendiken.
  • mulțumesc 888 pentru răspuns. Compilatorul MS are thread::id::hash(), dar codul lui Barry este conform standardelor. Hashurile se pot ciocni. Este totuși util să avem un hash pentru fiecare fir (cu o probabilitate de coliziune, sperăm, apropiată de 0) –  > Por a.lasram.
  • MSVC returnează de fapt un hashed în acest caz. Puteți la fel de bine să vă generați propriul… –  > Por rustyx.
Mike

Un alt id (idee? ^^) ar fi să folosiți stringstreams:

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

Și folosiți try catch dacă nu doriți o excepție în cazul în care lucrurile merg prost…

Comentarii

  • Bun răspuns. Acest lucru ar servi scopului în general. –  > Por iammilind.
  • Acest lucru nu este portabil, deoarece nu există nici o garanție că un std::thread::id se tipărește ca caractere care alcătuiesc un număr întreg, la fel cum nu este garantat faptul că id-ul firului este reprezentat în mod intern de un număr întreg. –  > Por blubberdiblub.
  • @Nikos ori de câte ori o implementare decide că un număr întreg este insuficient. Sau ori de câte ori consideră că este inadecvat din orice alt motiv. Ideea este că, atunci când specificația nu specifică faptul că este un întreg (și nu o face, are doar niște garanții mai abstracte), nu vă puteți și nu ar trebui să vă bazați pe faptul că este un întreg în nicio implementare. Utilizați pur și simplu std::thread::id ca tip în loc de un întreg, pentru asta există. Și nu reinterpretați reprezentarea sa în șiruri ca fiind cifrele care alcătuiesc un număr. Tratați-o ca fiind opacă sau ca ieșire de depanare/logging. –  > Por blubberdiblub.
tgoodhart

O idee ar fi să folosiți stocarea locală a firului pentru a stoca o variabilă – nu contează ce tip, atâta timp cât respectă regulile de stocare locală a firului – apoi să folosiți adresa acelei variabile ca „id al firului”. Evident, orice aritmetică nu va fi semnificativă, dar va fi un tip integral.

Pentru posteritate:pthread_self() returnează o pid_t și este posix. Acest lucru este portabil pentru o anumită definiție a portabilității.

gettid(), În mod aproape sigur nu este portabil, dar returnează o valoare prietenoasă pentru GDB.

Comentarii

  • pthread_self() returnează de fapt a pthread_t, , care este opacă (spre deosebire de pid_t (returnat de gettid()), care, deși este de asemenea specifică platformei, este aparent un număr întreg, cel puțin). Dar +1 pentru prima parte, mi-a rezolvat problema! –  > Por Cameron.
NoSenseEtAl

Chiar nu știu cât de rapid este acest lucru, dar aceasta este soluția pe care am reușit să o ghicesc :

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;

Din nou, încep să cred că obținerea unui pointer la structură și turnarea acestuia în unsigned int sau uint64_t este soluția…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 pentru a preveni problemele infernale 🙂 Rescrierea este ușoară în comparație cu vânarea acestui tip de bug. 🙂

Comentarii

  • Nu aveți nicio garanție că nu veți obține valori duplicate cu ajutorul funcției hash funcție, cu atât mai puțin dacă o folosești cu %.. –  > Por R. Martinho Fernandes.
  • Nu puteți obține această garanție cu std::this_thread::get_id()! Dar probabil că nu aveți nevoie de ea. Câteva fire de execuție care împart unul cu celălalt nu creează aceeași problemă masivă ca fiecare fir de execuție care împarte cu fiecare alt fir de execuție. Ceva de genul const size_t N_COUNTERS = 128; struct Counter { std::atomic<int> counter; char pad[CACHE_LINE_SIZE - sizeof(atomic<int>); } counters[N_COUNTERS]; este probabil în regulă. (Un atomic sau un spinlock pentru o sincronizare foarte ușoară).  > Por Scott Lamb.
  • @R. Martinho Fernandes După cum am spus, mă interesează valoarea int astfel încât să pot %, coliziunile sunt în regulă dacă sunt rare, în principiu ceea ce a spus Scott. –  > Por NoSenseEtAl.
  • De fapt, am încercat acest lucru și am greșit complet – doar folosind atomic<int> în loc de int este o încetinire dramatică chiar și fără contenție. –  > Por Scott Lamb.
  • Puteți înlocui static_assert cu ceva de genul următor ideone.com/Q7Nh4 (ușor de modificat pentru a impune o cerință de mărime exactă dacă doriți acest lucru) pentru a funcționa mai ușor de transportat (de exemplu, observați cum ideone are un id de fir pe 32 de biți). –  > Por R. Martinho Fernandes.
Alexey Polonsky

thread::native_handle() returnează thread::native_handle_type, , care este un tipedef pentru long unsigned int.

Dacă firul este construit în mod implicit, native_handle() returnează 0. Dacă există un fir OS atașat la el, valoarea de retur este diferită de zero (este pthread_t pe POSIX).

Comentarii

  • Unde se specifică faptul că std::thread::native_handle_type este un tipedef pentru long unsigned? În 30.3.1/1 putem vedea doar typedef implementation-defined native_handle_type; // See 30.2.3 –  > Por Ruslan.
  • O modalitate stupidă, dar simplă de a descoperi tipul este de a genera o eroare de compilare deliberată prin atribuirea thread::native_handle() la, de exemplu, uint8_t. Atunci compilatorul se va plânge de nepotrivire de tip și vă va spune și care este tipul. –  > Por Alexey Polonsky.
  • Ei bine, acest lucru nu este portabil, deoarece se bazează pe o anumită implementare. –  > Por Ruslan.
  • Ei bine, cel puțin dacă implementarea de bază folosește POSIX pthread, se pare că native_handle() trebuie să fie un pthread_t. Or, pthread_t este un tip de pointer (typedef struct pthread * pthread_t). Prin urmare, este logic ca std::thread::native_handle_type să fie un tip întreg capabil să conțină un pointer (de exemplu, size_t sau unsigned long). –  > Por Alexey Polonsky.
midjji

Un motiv cheie pentru a nu folosi thread::get_id() este că nu este unic pentru un singur program/proces. Acest lucru se datorează faptului că id-ul poate fi refolosit pentru un al doilea fir, odată ce primul fir se termină.

Aceasta pare a fi o caracteristică oribilă, dar este ceea ce este în c++11.

Federico Rizzo

În acest fel, ar trebui să funcționeze:

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

Nu uitați să includeți biblioteca sstream

Comentarii

  • Frumos, dar de ce presupui că este un număr întreg? Poate fi hexagonal sau orice altceva. –  > Por rustyx.
  • dacă folosiți std::stringstream, , atunci puteți folosi operator >> pentru a converti în int. De fapt, aș prefera uint64_t ca tip de id în loc de int dacă sunt sigur că id este integral. –  > Por aniliitb10.
6502

O altă variantă:

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

Codul generat pentru această funcție de g++ în x86 pe 64 de biți este doar:

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

Adică o singură ramură fără nicio sincronizare care va fi prezisă corect, cu excepția primei apelări a funcției. După aceea doar un singur acces la memorie fără sincronizare.

Comentarii

  • @NoSenseEtAl: Nu sunt sigur că am înțeles întrebarea ta… thread_local descrie deja durata de stocare pentru tid. static pentru thread_counter este pentru că nu doriți să o expuneți în afara acestei unități de compilare. –  > Por 6502.
  • Acest lucru atribuie în mod ciudat ID-urile de fir în ordinea în care interoghezi ID-ul de fir. (Am făcut și eu ceva FOARTE asemănător și nu mi-a plăcut niciodată această ciudățenie.) De asemenea, atribuie de la zero, ceea ce nu este obișnuit. (De exemplu, GDB raportează ID-urile firelor începând de la 1.) – –  > Por Swiss Frank.
  • @SwissFrank: este doar un număr și nu ar trebui să citești prea mult în valoarea returnată: nu există nicio modalitate legală de a ști că a fost atribuită atunci când ai interogat-o 🙂 . În ceea ce privește faptul că 0 este un ID valid, este o observație bună și poate fi rezolvată folosind în schimb preincrementul. Voi modifica răspunsul pentru a face acest lucru. –  > Por 6502.
  • Pentru cazul de utilizare corect, de exemplu, un număr rezonabil de fire de execuție, un sistem de logare și un om care îl folosește și care preferă lucruri precum „1” și „2” în loc de 0x7e8000499000 sau 140141742282496, aceasta este o soluție ridicol de elegantă. –  > Por Allan Bazinet.
Pandrei

depinde pentru ce vrei să folosești thread_id; poți folosi:

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

Acest lucru va genera un id unic în cadrul procesului tău; dar există o limitare: dacă lansezi mai multe instanțe ale aceluiași proces și fiecare dintre ele își scrie id-urile de thread într-un fișier comun, unicitatea thread_id-ului nu este garantată; de fapt, este foarte probabil să ai suprapuneri. în acest caz, poți face ceva de genul:

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

acum vi se garantează id-uri unice ale firelor de execuție în tot sistemul.

Comentarii

  • Funcția supraîncărcată operator<< poate imprima orice, , este greșit să presupunem că va imprima întotdeauna un număr întreg. –  > Por rustyx.
geh

Poate această soluție să fie de ajutor cuiva. Numiți-o o primă dată im main(). Avertisment: names crește la nesfârșit.

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

Comentarii

  • nu folosiți stringstream, este lent, folosiți std::to_string –  > Por NoSenseEtAl.