C ++ 11 में पूर्णांक थ्रेड आईडी कैसे प्राप्त करें


84

c ++ 11 में वर्तमान थ्रेड आईडी होने की संभावना है, लेकिन यह पूर्णांक प्रकार के लिए उपयुक्त नहीं है:

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

आउटपुट: 139918771783456

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

त्रुटि: अमान्य कास्ट 'एसटीडी :: थ्रेड :: आईडी' से टाइप करने के लिए 'uint64_t' टाइप अन्य के लिए समान: 'एसटीडी :: थ्रेड :: आईडी' टाइप 'uint32_t' से अमान्य कास्ट

मैं वास्तव में पूर्णांक थ्रेड आईडी प्राप्त करने के लिए सूचक कास्टिंग नहीं करना चाहता। क्या कोई उचित तरीका है (मानक क्योंकि मैं चाहता हूं कि यह पोर्टेबल हो) इसे करने के लिए?


13
इसके लिए पूर्णांक होने के लिए आपको क्या चाहिए? यह गारंटी दी जाती है कि इस पर किसी भी प्रकार की अंकगणित करने के लिए समझदारी नहीं है, और यह प्रक्रिया के संदर्भ के बाहर सार्थक नहीं है, इसलिए डिबगिंग के लिए इसे (जिसे operator<<ठीक से लगता है) को छोड़कर अन्य को क्रमबद्ध करने की आवश्यकता नहीं होनी चाहिए ।
हमखोलम मोनिका पर छोड़ दिया

4
कुछ इस तरह से: 1024cores.net/home/lock-free-algorithms/false-sharing---false लेकिन N = MAX_THREAD_COUNT के बजाय मेरे पास N = 128 जैसा कुछ होगा और थ्रेड-रीड% N
NoSenseEtAl

9
यदि आप वास्तव में इसे पोर्टेबल होना चाहते हैं, तो आपको उस संभावना के लिए तैयार रहने की आवश्यकता है thread::idजो पूर्णांक के रूप में प्रतिनिधित्व नहीं करती है। थ्रेड आईडी द्वारा अनुक्रमित किसी सरणी का उपयोग करने के लिए आप जिस पृष्ठ से लिंक करते हैं। क्या आपने map<thread::id, int>इसके बजाय उपयोग करने पर विचार किया है? तब आप idबिना किसी रूपांतरण के वर्ग के लिए पहले से परिभाषित संबंधपरक ऑपरेटरों का उपयोग कर सकते हैं । मानक भी परिभाषित करता है hash<thread::id>, इसलिए आप अनियंत्रित कंटेनरों का भी उपयोग कर सकते हैं।
रॉब कैनेडी

3
@ रब उस नक्शे की आवश्यकता होगी म्यूटेक्सिंग :(
NoSenseEtAl

1
@SwissFrank या मुझे CHF कहना चाहिए: PI अभी भी चारों ओर है, लेकिन मुझे लगता है कि स्वीकृत उत्तर मेरे लिए ठीक है, यह सुनिश्चित करना मेरे लिए है कि किसी प्रोग्राम की अवधि के लिए वैरिएबल आईडी मान अद्वितीय हैं।
NoSenseEtAl

जवाबों:


33

पोर्टेबल समाधान अपने स्वयं के उत्पन्न आईडी को धागे में पारित करना है।

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अपनी खुद की आईडी के लिए मानों के मानचित्र का उपयोग भी कर सकते हैं , और सीधे इस आईडी को साझा करने के बजाय, थ्रेड्स के बीच इस नक्शे (उचित सिंक्रनाइज़ेशन के साथ) को साझा कर सकते हैं।


1
अहा! लेकिन वहाँ है एक पाठ प्रतिनिधित्व! मनुष्यों के लिए नेत्रहीन रूप से उनके बीच भेद करने के लिए यह काफी अच्छा है, है ना?
Xunie

यहाँ वर्णित थ्रेड :: id (या this_thread :: get_id ()) समाधान सबसे अच्छा है, क्योंकि यह प्रोग्रामर-विशिष्ट नहीं है। स्ट्रिंग या पूर्णांक प्रतिनिधित्व प्राप्त करने के लिए नीचे माइक का स्ट्रिंगस्ट्रीम उत्तर देखें।
एंड्रयू

@ और मैंने जवाब में कहा कि: "यहां तक ​​कि ऑपरेटर द्वारा निर्मित इसका पाठ प्रतिनिधित्व भी अपरिष्कृत है, इसलिए आप इस पर भरोसा नहीं कर सकते हैं कि यह संख्या का प्रतिनिधित्व है"। शब्द "छाया" की एक छायादार परिभाषा की तरह है।
आर। मार्टिनो फर्नांडिस

"सर्वश्रेष्ठ" स्ट्रिंग प्रतिनिधित्व के संबंध में नहीं था।
एंड्रयू

1
इसके अलावा, मैंने अभी-अभी अपने खुद के लिए 10,000,000 पुनरावृत्तियों के साथ एक बेंचमार्क किया था और this_thread :: get_id () तेजी से दुष्ट है: pastebin.com/eLa3rKQE डिबग मोड में प्रति कॉल 0.000000254384 सेकंड लेता है और रिलीज़ में मेरे लिए प्रति कॉल 0.00000003652367 सेकंड लगते हैं। (इंटेल i5 2.60 गीगाहर्ट्ज़)
एंड्रयू

85

आपको बस करने की जरूरत है

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

पाने के लिए a size_t

से cppreference :

वर्ग के std::hashलिए टेम्पलेट विशेषज्ञता std::thread::idउपयोगकर्ताओं को थ्रेड्स के पहचानकर्ताओं के हैश प्राप्त करने की अनुमति देती है।


35
मुझे लगता है कि यह होना चाहिए std::hash<std::thread::id>()(std::this_thread::get_id()), है ना?
बैरी

12
क्या हैश की गारंटी अद्वितीय होगी? शायद नहीं, एक अद्वितीय धागा पहचानकर्ता के रूप में इसके उपयोग को हराया।
माइकल गोल्डस्मीन

2
दिया गया उदाहरण कम से कम क्लैंग 3.4 और libstdc ++ 4.8 के साथ काम नहीं करता है। बैरी का सुधार काम करता है, हालांकि।
आर्टो बेंडिकेन

3
उत्तर के लिए धन्यवाद 888। एमएस कंपाइलर में थ्रेड :: आईडी :: हैश () होता है, लेकिन बैरी का कोड मानकों के अनुरूप होता है। दुर्गंध टकरा सकती है। यह अभी तक उपयोगी है एक धागा प्रति हैश (उम्मीद के साथ 0 के पास एक टक्कर संभावना)
a.lasram

1
MSVC वास्तव में इस मामले में एक हैशेड थ्रेड आईडी देता है। आप के रूप में अच्छी तरह से अपने खुद के उत्पन्न हो सकता है ...
rustyx

25

एक और आईडी (विचार; ^ ^) स्ट्रिंगस्ट्रीम का उपयोग करने के लिए होगा:

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

और यदि आप मामले में कोई गलती नहीं चाहते हैं तो अपवाद को पकड़ने का प्रयास करें ...


2
अच्छा उत्तर। यह सामान्य रूप से उद्देश्य पूरा करेगा।
इमिलिंड

5
यह पोर्टेबल नहीं है, क्योंकि इसमें कोई गारंटी नहीं है कि std::thread::idवर्णों के रूप में एक प्रिंट जो एक पूर्णांक को उसी तरह बनाता है, जो यह गारंटी नहीं देता है कि थ्रेड आईडी आंतरिक रूप से पूर्णांक द्वारा दर्शाया गया है।
ब्लबरडाइबल जुब

1
@ निकोस जब भी कोई क्रियान्वयन चुनता है कि पूर्णांक अपर्याप्त है। या जब भी किसी अन्य कारण से यह अनुचित लगता है। यहाँ मुद्दा यह है कि जब विनिर्देश इसे पूर्णांक के रूप में निर्दिष्ट नहीं करता है (और यह नहीं है, तो इसमें कुछ और सार गारंटी है), आप किसी भी कार्यान्वयन में पूर्णांक होने पर भरोसा नहीं कर सकते हैं और नहीं करना चाहिए। बस std::thread::idकुछ पूर्णांक के बजाय टाइप के रूप में उपयोग करें , कि यह किस लिए मौजूद है। और अंक बनाने के रूप में इसके स्ट्रिंग प्रतिनिधित्व को फिर से व्याख्या न करें। इसे अपारदर्शी या डिबगिंग / लॉगिंग आउटपुट के रूप में समझें।
ब्लबरडाइब्लूब

6

एक विचार यह होगा कि वेरिएबल को स्टोर करने के लिए थ्रेड लोकल स्टोरेज का उपयोग किया जाए - इससे कोई फर्क नहीं पड़ता कि वह किस प्रकार का है, इसलिए जब तक वह थ्रेड लोकल स्टोरेज के नियमों का पालन करता है - तब उस वेरिएबल के एड्रेस का उपयोग आपकी "थ्रेड आईडी" के रूप में करना है। जाहिर है कि कोई भी शब्दार्थक सार्थक नहीं होगा, लेकिन यह एक अभिन्न प्रकार होगा।

पश्चाताप के लिए: pthread_self()रिटर्न ए pid_tऔर पॉज़िक्स है। यह पोर्टेबल की कुछ परिभाषा के लिए पोर्टेबल है।

gettid(), लगभग निश्चित रूप से पोर्टेबल नहीं है, लेकिन यह एक GDB अनुकूल मूल्य लौटाता है।


pthread_self()वास्तव में एक रिटर्न देता है pthread_t, जो अपारदर्शी है (विपरीत pid_tद्वारा लौटाया गया है gettid()), जबकि प्लेटफ़ॉर्म-विशिष्ट, जाहिरा तौर पर एक पूर्णांक है, कम से कम)। लेकिन पहली बिट के लिए, यह मेरी समस्या हल हो गई!
कैमरन

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;

फिर से मुझे लगता है कि संरचना के लिए एक सूचक हो रही है और यह अहस्ताक्षरित int या uint64_t को जवाब दे रहा है ... EDD:

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फ़ंक्शन के साथ डुप्लिकेट मान नहीं मिलेगा , यदि आप इसे% कम करते हैं
आर। मार्टिनो फर्नांडिस

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];शायद कुछ ठीक है। (बहुत हल्के सिंक्रोनाइज़ेशन के लिए एक परमाणु या स्पिनलॉक।)
स्कॉट लैम

@R। मार्टिनहो फर्नांडिस लाइक ने कहा कि मैं इंट वैल्यू में दिलचस्पी रखता हूं इसलिए मैं इसे% कर सकता हूं, टकराव ठीक हैं अगर वे दुर्लभ हैं, तो मूल रूप से स्कॉट ने क्या कहा।
NoSenseEtAl

1
मैंने वास्तव में यह कोशिश की थी और मैं पूरी तरह से गलत था - बस atomic<int>इसके बजाय intएक नाटकीय मंदी का उपयोग करना है , यहां तक ​​कि बिना किसी विवाद के भी।
स्कॉट लैंब

1
आप static_assert को इस ideone.com/Q7Nh4 जैसे कुछ से बदल सकते हैं (आसानी से एक सटीक आकार की आवश्यकता को लागू करने के लिए यदि आप इसके बजाय चाहते हैं) तो इसे और अधिक बेहतर तरीके से काम करने के लिए नोट करें (ध्यान दें कि कैसे आइडोन में 32-बिट आईडी है, उदाहरण के लिए) ।
आर। मार्टिनो फर्नांडिस

4

thread::native_handle()प्रतिफल thread::native_handle_type, जो कि एक टंकण है long unsigned int

यदि थ्रेड डिफ़ॉल्ट रूप से निर्मित है, तो native_handle () 0. लौटाता है। यदि इसमें कोई OS थ्रेड संलग्न है, तो रिटर्न मान नॉन-जीरो है (यह POSIX पर pthread_t है)।


यह कहां निर्दिष्ट किया गया है कि std::thread::native_handle_typeकिसके लिए एक टंकण है long unsigned? 30.3.1 / 1 में हम केवल देख सकते हैंtypedef implementation-defined native_handle_type; // See 30.2.3
रुस्लान

प्रकार की खोज करने के लिए एक गूंगा लेकिन सरल तरीका यह है कि थ्रेड :: native_handle () जैसे uint8_t को असाइन करके एक जानबूझकर संकलन त्रुटि उत्पन्न करें। फिर कंपाइलर टाइप मिसमैच की शिकायत करेगा और आपको यह भी बताएगा कि टाइप क्या है।
एलेक्सी पोल्स्की

1
खैर यह गैर-पोर्टेबल है क्योंकि यह विशेष रूप से कार्यान्वयन पर निर्भर करता है।
रुस्लान

ठीक है, कम से कम यदि अंतर्निहित कार्यान्वयन POSIX pthread का उपयोग करता है, तो ऐसा लगता है कि native_handle () एक pthreadtt होना चाहिए। अब, pthread_t एक पॉइंटर प्रकार (टाइपेडिफ स्ट्रक्चर pthread * pthread_t) है। तो, यह समझ में आता है कि std :: thread :: native_handle_type एक पूर्णांक प्रकार है जो एक पॉइंटर (जैसे size_t या अहस्ताक्षरित लंबे) को रखने में सक्षम है।
एलेक्सी पोलोनस्की

3

इस तरह से काम करना चाहिए:

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

लाइब्रेरी सिस्ट को शामिल करना याद रखें


अच्छा लगा, लेकिन आप इसे पूर्णांक क्यों मानते हैं? यह हेक्स या कुछ और हो सकता है।
रस्टीक्स

यदि आप उपयोग कर रहे हैं std::stringstream, तो आप इसका उपयोग operator >>int में बदलने के लिए कर सकते हैं । मैं वास्तव uint64_tमें idइसके बजाय के प्रकार के रूप में पसंद करता intहूँ अगर मुझे यकीन है कि idअभिन्न है।
ailiitb10

3

धागे का उपयोग नहीं करने का एक महत्वपूर्ण कारण :: get_id () यह है कि यह एकल प्रोग्राम / प्रक्रिया के लिए अद्वितीय नहीं है। ऐसा इसलिए है क्योंकि पहले धागे के खत्म होने के बाद आईडी को दूसरे धागे के लिए पुन: उपयोग किया जा सकता है।

यह एक भयानक विशेषता की तरह लगता है, लेकिन सी ++ 11 में इसका क्या है।


2

यह इस बात पर निर्भर करता है कि आप क्या उपयोग करना चाहते हैं। आप उपयोग कर सकते हैं:

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

यह आपको प्रक्रिया के साथ एक अद्वितीय आईडी उत्पन्न करेगा; लेकिन एक सीमा है: यदि आप एक ही प्रक्रिया के कई उदाहरण लॉन्च करते हैं और उनमें से प्रत्येक एक सामान्य फ़ाइल में अपने थ्रेड आईडी लिखते हैं, थ्रेड की अद्वितीयता की गारंटी नहीं है; वास्तव में यह बहुत संभावना है कि आपके पास ओवरलैप होंगे। इस मामले में आप कुछ ऐसा कर सकते हैं:

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

अब आपको विशिष्ट थ्रेड आईडी सिस्टमव्यापी गारंटी दी गई है।


ओवरलोडेड कुछ भीoperator<< प्रिंट कर सकता है , यह मान लेना गलत है कि यह हमेशा एक पूर्णांक प्रिंट करेगा।
रस्टीक्स

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

इस फ़ंक्शन के लिए x 86 64-बिट में जनरेट किया गया कोड है:

_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पहले से ही भंडारण अवधि का वर्णन करता है tidstaticके लिए thread_counterहै, क्योंकि आप इस संकलन इकाई के बाहर यह बेनकाब करने के लिए नहीं करना चाहती।
6502

इस तरह के अजीब तरीके से आप थ्रेड आईडी के लिए थ्रेड आईडी असाइन करते हैं। (मैंने कुछ ऐसा ही किया है, और मुझे यह अजीब नहीं लगा।) यह शून्य से भी असाइन होता है, जो सामान्य नहीं है। (उदाहरण के लिए GDB रिपोर्ट थ्रेड आईडी 1 से शुरू होती है)
स्विस फ्रैंक

1
@SwissFrank: यह सिर्फ एक संख्या है और आपको लौटाए गए मान में बहुत अधिक नहीं पढ़ना चाहिए: यह जानने का कोई कानूनी तरीका नहीं है कि जब आपने इसे उद्धृत किया था, तो यह :-) था। इस तथ्य के बारे में कि 0एक वैध आईडी है जो एक अच्छा बिंदु है और इसके बजाय पूर्वनिर्धारण का उपयोग करके तय किया जा सकता है। मैं ऐसा करने के लिए उत्तर बदल दूंगा।
6502

1

हो सकता है कि यह समाधान किसी के लिए सहायक हो। इसे पहली बार im कहें 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];
}

स्ट्रिंगस्ट्रीम का उपयोग न करें, यह धीमा है, std का उपयोग करें :: to_string
NoSenseEtAl
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.