【发布时间】:2010-12-18 02:29:17
【问题描述】:
已搜索,但没有找到令人满意的答案。
我知道没有可移植的方式来打印 pthread_t。
你是如何在你的应用中做到这一点的?
更新:
实际上我不需要 pthread_t,而是一些小的数字 id,在调试消息中标识不同的线程。
在我的系统(64 位 RHEL 5.3)上,它被定义为 unsigned long int,所以它是一个很大的数字,仅仅打印它就在调试行中占据了一个有价值的位置。 gdb 如何分配短时间?
【问题讨论】:
已搜索,但没有找到令人满意的答案。
我知道没有可移植的方式来打印 pthread_t。
你是如何在你的应用中做到这一点的?
更新:
实际上我不需要 pthread_t,而是一些小的数字 id,在调试消息中标识不同的线程。
在我的系统(64 位 RHEL 5.3)上,它被定义为 unsigned long int,所以它是一个很大的数字,仅仅打印它就在调试行中占据了一个有价值的位置。 gdb 如何分配短时间?
【问题讨论】:
这将打印出pthread_t 的十六进制表示,无论它实际上是什么:
void fprintPt(FILE *f, pthread_t pt) {
unsigned char *ptc = (unsigned char*)(void*)(&pt);
fprintf(f, "0x");
for (size_t i=0; i<sizeof(pt); i++) {
fprintf(f, "%02x", (unsigned)(ptc[i]));
}
}
要为每个 pthread_t 打印一个小 id,可以使用类似的东西(这次使用 iostreams):
void printPt(std::ostream &strm, pthread_t pt) {
static int nextindex = 0;
static std::map<pthread_t, int> ids;
if (ids.find(pt) == ids.end()) {
ids[pt] = nextindex++;
}
strm << ids[pt];
}
根据平台和pthread_t 的实际表示,这里可能需要为pthread_t 定义一个operator<,因为std::map 需要对元素进行排序:
bool operator<(const pthread_t &left, const pthread_t &right) {
...
}
【讨论】:
ptc 指针在 fprintf 行中被取消引用。
unsigned char* 之前转换为void* 的目的是什么?为什么第一个演员不是多余的?
GDB 在 Linux 上使用线程 ID(又名内核 pid,又名 LWP)作为短数字。试试:
#include <syscall.h>
...
printf("tid = %d\n", syscall(SYS_gettid));
【讨论】:
在这种情况下,它取决于操作系统,因为the POSIX standard no longer requires pthread_t to be an arithmetic type:
IEEE Std 1003.1-2001/Cor 2-2004, item XBD/TC2/D6/26 被应用,将
pthread_t添加到不需要是算术类型的类型列表中,从而允许pthread_t定义为结构。
您需要查看sys/types.h 标头并查看pthread_t 是如何实现的;然后您可以按照您认为合适的方式打印它。由于没有可移植的方式来执行此操作,并且您没有说明您使用的是什么操作系统,所以没有太多要说的了。
编辑:回答您的新问题,GDB assigns its own thread ids each time a new thread starts:
出于调试目的,gdb 将其自己的线程号(始终为单个整数)与程序中的每个线程相关联。
如果您希望在每个线程内打印一个唯一编号,您最简洁的选择可能是告诉每个线程在启动时使用什么编号。
【讨论】:
pthread_create,我没有任何反应。如何做到这一点?
pthread_create 的第四个参数允许您将任意数据传递给线程过程。
好的,看来这是我的最终答案。我们有两个实际问题:
1.打印 POSIX ID (pthread_t)
您可以简单地将 pthread_t 视为字节数组,并为每个字节打印十六进制数字。因此,您不受某些固定尺寸类型的限制。唯一的问题是字节顺序。您可能喜欢打印字节的顺序是否与打印的简单“int”相同。以下是 little-endian 的示例,只有 order 应该为 big-endian 恢复(在定义下?):
#include <pthread.h>
#include <stdio.h>
void print_thread_id(pthread_t id)
{
size_t i;
for (i = sizeof(i); i; --i)
printf("%02x", *(((unsigned char*) &id) + i - 1));
}
int main()
{
pthread_t id = pthread_self();
printf("%08x\n", id);
print_thread_id(id);
return 0;
}
2。获取更短的可打印线程 ID
在任何建议的情况下,您都应该将真实线程 ID (posix) 转换为某个表的索引。但是有两种截然不同的方法:
2.1.跟踪线程。
您可以跟踪表中所有现有线程的线程 ID(应包装它们的 pthread_create() 调用)并具有“重载”的 id 函数,该函数只为您获取表索引,而不是真正的线程 ID。该方案对于任何与内部线程相关的调试和资源跟踪也非常有用。明显的优势是线程级跟踪/调试工具的副作用,未来可能扩展。缺点是需要跟踪任何线程的创建/销毁。
这里是部分伪代码示例:
pthread_create_wrapper(...)
{
id = pthread_create(...)
add_thread(id);
}
pthread_destruction_wrapper()
{
/* Main problem is it should be called.
pthread_cleanup_*() calls are possible solution. */
remove_thread(pthread_self());
}
unsigned thread_id(pthread_t known_pthread_id)
{
return seatch_thread_index(known_pthread_id);
}
/* user code */
printf("04x", thread_id(pthread_self()));
2.2.只需注册新的线程 ID。
在记录过程中调用 pthread_self() 并搜索内部表是否知道线程。 如果创建了具有此类 ID 的线程,则使用其索引(或从以前的线程中重新使用,实际上这并不重要,因为同一时刻没有 2 个相同的 ID)。如果尚不知道线程 ID,则会创建新条目以便生成/使用新索引。
优点是简单。缺点是没有跟踪线程创建/销毁。因此,要跟踪这一点,需要一些外部机制。
【讨论】:
在 Centos 5.4 x86_64 上,pthread_t 是 unsigned long 的 typedef。
因此,我们可以这样做...
#include <iostream>
#include <pthread.h>
int main() {
pthread_t x;
printf("%li\n", (unsigned long int) x);
std::cout << (unsigned long int) x << "\n";
}
【讨论】:
你可以这样做:
int thread_counter = 0;
pthread_mutex_t thread_counter_lock = PTHREAD_MUTEX_INITIALIZER;
int new_thread_id() {
int rv;
pthread_mutex_lock(&thread_counter_lock);
rv = ++thread_counter;
pthread_mutex_unlock(&thread_counter_lock);
return rv;
}
static void *threadproc(void *data) {
int thread_id = new_thread_id();
printf("Thread %d reporting for duty!\n", thread_id);
return NULL;
}
如果您可以依赖 GCC(clang 在这种情况下也可以使用),您也可以这样做:
int thread_counter = 0;
static void *threadproc(void *data) {
int thread_id = __sync_add_and_fetch(&thread_counter, 1);
printf("Thread %d reporting for duty!\n", thread_id);
return NULL;
}
如果你的平台支持这个,类似的选项:
int thread_counter = 0;
int __thread thread_id = 0;
static void *threadproc(void *data) {
thread_id = __sync_add_and_fetch(&thread_counter, 1);
printf("Thread %d reporting for duty!\n", thread_id);
return NULL;
}
这样做的好处是您不必在函数调用中传递 thread_id,但它不起作用,例如在 Mac 操作系统上。
【讨论】:
如果 pthread_t 只是一个数字;这将是最简单的。
int get_tid(pthread_t tid)
{
assert_fatal(sizeof(int) >= sizeof(pthread_t));
int * threadid = (int *) (void *) &tid;
return *threadid;
}
【讨论】:
查找表(pthread_t : int) 可能会在启动大量短期线程的程序中成为内存泄漏。
创建pthread_t 字节的哈希(无论是结构、指针还是长整数或其他)可能是不需要查找表的可用解决方案。与任何散列一样,存在冲突的风险,但您可以调整散列的长度以满足您的要求。
【讨论】:
pthread_t 值来引用它怎么办?
我知道,这个帖子很老了。阅读完以上所有帖子后,我想再添加一个想法来以一种简洁的方式处理这个问题: 如果您无论如何都进入映射业务(将 pthread_to 映射到 int),那么您还可以在可读性方面更进一步。使您的 pthread_create_wrapper 也接受一个字符串......线程的名称。 我学会了欣赏 Windows 和 Windows CE 上的“SetThreadName()”功能。 优点:您的 id 不仅是数字,而且您还可以看到,您的哪些线程有哪些用途。
【讨论】:
您可以尝试将其转换为无符号短整数,然后仅打印最后四个十六进制数字。生成的值可能对您的需求足够独特。
【讨论】:
不用花哨,你可以把pthread_t当成一个不用强制转换的指针:
printf("awesome worker thread id %p\n", awesome_worker_thread_id);
输出:
很棒的工作线程 id 0x6000485e0
【讨论】:
只是对第一篇文章的补充:使用用户定义的联合类型来存储pthread_t:
union tid {
pthread_t pthread_id;
unsigned long converted_id;
};
当你想打印pthread_t时,创建一个tid并分配tid.pthread_id = ...,然后打印tid.converted_id。
【讨论】:
pthread_equal 这样的函数。
正如 James 上面提到的,最好的方法是查看定义类型的标头。
您可以在 pthreadtypes.h 中找到 pthread_t 的定义,该文件位于:
/usr/include/bits/pthreadtypes.h
【讨论】: