【问题标题】:How to get integer thread id in c++11如何在 C++11 中获取整数线程 ID
【发布时间】:2011-11-17 22:15:18
【问题描述】:

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。有没有一些合理的方法(标准,因为我希望它是可移植的)来做到这一点?

【问题讨论】:

  • 你需要它作为整数做什么?保证对其进行任何类型的算术运算都没有意义,并且在进程的上下文之外没有意义,因此除了调试之外不需要序列化它(operator&lt;&lt; 似乎处理得很好)。
  • 类似这样的东西:1024cores.net/home/lock-free-algorithms/false-sharing---false 但不是 N=MAX_THREAD_COUNT 我会有类似 N=128 的东西并执行 thread_id%N
  • 如果您真的希望它是可移植的,那么您需要为thread::id 根本不表示为整数的可能性做好准备。您链接到的页面使用一个数组,由线程 ID 索引。您是否考虑过改用map&lt;thread::id, int&gt;?然后,您可以使用已经为 id 类定义的关系运算符,而无需进行任何转换。该标准还定义了hash&lt;thread::id&gt;,因此您也可以使用无序容器。
  • @Rob 该地图需要互斥锁 :(
  • @SwissFrank 或者我应该说 CHF :PI 还在,但我认为接受的答案对我来说没问题,我有责任确保变量 id 值在一个程序。

标签: c++ multithreading c++11


【解决方案1】:

你只需要这样做

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

获取size_t

来自cppreference

std::hash 的模板特化 std::thread::id 类允许用户获取线程标识符的哈希值。

【讨论】:

  • 我想这一定是std::hash&lt;std::thread::id&gt;()(std::this_thread::get_id()),不是吗?
  • 哈希值能保证唯一吗?可能不会,因为它无法用作唯一的线程标识符。
  • 给出的示例至少不适用于 Clang 3.4 和 libstdc++ 4.8。然而,巴里的重新表述确实有效。
  • 感谢 888 的回答。 MS 编译器确实有 thread::id::hash() 但 Barry 的代码符合标准。哈希可能会发生冲突。每个线程都有一个散列还是很有用的(希望碰撞概率接近 0)
  • MSVC 在这种情况下实际上返回了一个 hashed 线程 ID。你不妨生成自己的...
【解决方案2】:

便携式解决方案是将您自己生成的 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&lt;&lt; 生成的文本表示也未指定,因此您不能依赖它作为数字的表示。

您还可以将std::thread::id 值映射到您自己的 id,并在线程之间共享此映射(通过适当的同步),而不是直接传递 id。

【讨论】:

  • 啊哈!但是有一个文本表示!这足以让人类在视觉上找到它们之间的区别,对吧?
  • 这里提到的thread::id(或this_thread::get_id())解决方案是最好的,因为它不是特定于程序员的。请参阅下面 Mike 的 stringstream 答案以获取字符串或整数表示形式。
  • @Andrew 我在回答中提到了这一点:“即使它由 operator
  • "best" 与字符串表示无关。
  • 另外,为了我自己,我刚刚做了一个 10,000,000 次迭代的基准测试,this_thread::get_id() 非常快:pastebin.com/eLa3rKQE 调试模式每次调用需要 0.0000002543827 秒,每次调用释放需要 0.00000003652367 秒为了我。 (英特尔 i5 2.60 GHz)
【解决方案3】:

另一个 id(想法?^^)是使用字符串流:

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

如果您不想在出现异常的情况下出现异常,请使用 try catch...

【讨论】:

  • 好答案。这通常可以达到目的。
  • 这是不可移植的,因为不能保证 std::thread::id 打印为组成整数的字符,其方式与不能保证线程 ID 在内部由整数表示的方式大致相同。
  • @Nikos 每当实现选择整数不足时。或者当它出于任何其他原因认为它不合适时。这里的要点是,当规范没有将它指定为整数时(它没有,它只是有一些更抽象的保证),你不能也不应该在任何实现中依赖它作为整数。只需使用std::thread::id 作为类型而不是某个整数,这就是它存在的目的。并且不要将其字符串表示重新解释为组成数字的数字。将其视为不透明或调试/日志输出。
【解决方案4】:

一个想法是使用线程本地存储来存储一个变量——不管是什么类型,只要它符合线程本地存储的规则——然后使用该变量的地址作为你的“线程ID” ”。显然任何算术都没有意义,但它会是一个整数类型。

为了后代: pthread_self() 返回 pid_t 并且是 posix。对于便携的某些定义,这是便携的。

gettid(),几乎可以肯定是不可移植的,但它确实返回了对 GDB 友好的值。

【讨论】:

  • pthread_self() 实际上返回一个pthread_t,它是不透明的(不像pid_t(由gettid() 返回),虽然它也是特定于平台的,但至少显然是一个整数)。但是首先 +1,它解决了我的问题!
【解决方案5】:

不使用 thread::get_id() 的一个关键原因是它在单个程序/进程中不是唯一的。这是因为一旦第一个线程完成,该 id 就可以被第二个线程重用。

这似乎是一个可怕的功能,但它是 c++11 中的。

【讨论】:

    【解决方案6】:

    我真的不知道这有多快,但这是我设法猜测的解决方案:

    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 来防止地狱般的问题 :) 与寻找这种错误相比,重写很容易。 :)

    【讨论】:

    • 您无法保证使用hash 函数不会获得重复值,如果您使用 % 则更少
    • std::this_thread::get_id() 无法获得该保证!但你可能不需要它。彼此共享的几个线程不会产生与每个线程与其他线程共享相同的大问题。像const size_t N_COUNTERS = 128; struct Counter { std::atomic&lt;int&gt; counter; char pad[CACHE_LINE_SIZE - sizeof(atomic&lt;int&gt;); } counters[N_COUNTERS]; 这样的东西可能没问题。 (用于非常轻量级同步的原子或自旋锁。)
    • @R. Martinho Fernandes 就像我说的那样,我对 int 值感兴趣,所以我可以 % 它,如果它们很少发生碰撞是可以的,基本上是 Scott 所说的。
    • 我实际上尝试过这个,但我完全错了 - 即使没有争用,只使用 atomic&lt;int&gt; 而不是 int 是一个戏剧性的减速。
    • 您可以将 static_assert 替换为类似 ideone.com/Q7Nh4 的东西(如果您愿意,可以轻松调整以强制执行确切的大小要求)使其更便携(注意 ideone 如何具有 32 位例如线程 ID)。
    【解决方案7】:

    这样,应该可以了:

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

    记得包含库流

    【讨论】:

    • 很好,但你为什么认为它是一个整数?它可以是十六进制或其他任何值。
    • 如果你使用std::stringstream,那么你可以使用它的operator &gt;&gt;来转换成int。如果我确定id 是不可或缺的,我实际上更喜欢uint64_t 作为id 的类型而不是int
    【解决方案8】:

    thread::native_handle() 返回thread::native_handle_type,这是long unsigned int 的类型定义。

    如果线程是默认构造的,native_handle() 返回 0。 如果有一个 OS 线程附加到它,则返回值非零(在 POSIX 上为 pthread_t)。

    【讨论】:

    • 在哪里指定std::thread::native_handle_typelong unsigned 的typedef?在 30.3.1/1 我们只能看到typedef implementation-defined native_handle_type; // See 30.2.3
    • 发现类型的一种愚蠢但简单的方法是通过将 thread::native_handle() 分配给例如uint8_t。然后编译器会抱怨类型不匹配,还会告诉你类型是什么。
    • 那是不可移植的,因为它依赖于特定的实现。
    • 好吧,至少如果底层实现使用POSIX pthread,似乎native_handle() 必须是pthread_t。现在,pthread_t 是一个指针类型(typedef struct pthread * pthread_t)。因此,std::thread::native_handle_type 是能够包含指针的整数类型(例如 size_t 或 unsigned long)是有道理的。
    【解决方案9】:

    另一种选择:

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

    g++ 在 x86 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 已经描述了tid 的存储持续时间。 thread_counterstatic 是因为您不想在此编译单元之外公开它。
    • 这种奇怪的按照你查询线程ID的顺序分配线程ID。 (我自己也做过非常相似的事情,而且我从不喜欢这种怪异之处。)它也从零开始赋值,这并不常见。 (例如 GDB 报告从 1 开始的线程 ID。)
    • @SwissFrank:它只是一个数字,你不应该在返回的值中读太多:没有合法的方法可以知道它是在你查询它时分配的:-)。关于 0 是一个有效的 ID 的事实,这是一个很好的观点,可以使用 preincrement 来修复。我会改变答案来做到这一点。
    • 对于正确的用例,例如,合理有限数量的线程、日志系统以及使用它的人更喜欢“1”和“2”而不是 0x7e8000499000 或 140141742282496,这个只是一个非常优雅的解决方案。
    • 我使用了非常相似的东西:int GetThreadId() { static std::atomic_int threadCount{0}; thread_local int threadId = threadCount.fetch_add(1); return threadId; } 你可以将threadCount 初始化为任何你喜欢的值(例如1)。
    【解决方案10】:

    这取决于你想用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&lt;&lt; 可以打印anything,假设它总是打印一个整数是错误的。
    【解决方案11】:

    也许这个解决方案对某人有帮助。第一次打电话给我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,它很慢,使用std::to_string
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-14
    • 2014-02-01
    • 2010-10-29
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多