我知道没有一种便携式的方法来打印pthread_t。
您是如何在应用程序中实现的?
更新:
实际上,我不需要pthread_t,而只是一些小的数字ID,在调试消息中标识不同的线程。
在我的系统(64位RHEL 5.3)中,它被定义为无符号长整型,因此它是一个大数字,仅仅打印它会占据调试行中的有价值的位置。 gdb如何分配短TID?
不管 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) {
...
}
GDB在Linux系统中使用线程ID(也称内核PID或LWP)表示短数字。尝试以下命令:
#include <syscall.h>
...
printf("tid = %d\n", syscall(SYS_gettid));
pthread_t
是算术类型:
您需要查看您的IEEE Std 1003.1-2001/Cor 2-2004,应用项目 XBD/TC2/D6/26,将
pthread_t
添加到不需要是算术类型的类型列表中,从而允许将pthread_t
定义为结构。
sys/types.h
标头并查看如何实现 pthread_t
;然后可以按照您认为合适的方式打印它。由于没有便携式的方法来执行此操作,并且您没有说明使用的操作系统,因此没有更多可说的。
编辑:回答您的新问题,每次启动新线程时,GDB都会分配自己的线程ID:
如果您正在考虑在每个线程内打印唯一编号,则最清晰的选择可能是在启动线程时告诉每个线程要使用的编号。出于调试目的,gdb将其自己的线程号(始终是单个整数)与程序中的每个线程相关联。
pthread_create
,但没有什么引起我的注意。如何做到这一点? - jwwpthread_create
的第四个参数允许您将任意数据传递给线程过程。 - James McNellis好的,看起来这是我的最终答案。我们有两个实际问题:
1. 打印POSIX ID(pthread_t)
您可以将pthread_t简单地视为字节数组,并为每个字节打印十六进制数字。因此,您不受某些固定大小类型的限制。唯一的问题是字节顺序。您可能希望您打印的字节顺序与简单的“int”打印的顺序相同。以下是小端示例,仅需反转顺序(在#define下?)以适应大端:
#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,则创建新条目,因此生成/使用新索引。
优点是简单易行。缺点是无法对线程的创建/销毁进行跟踪。因此需要一些外部机制来跟踪这一点。
pthread_t
不是无符号整数时,printf("%08x\n", id);
是未定义的行为。为了避免这种情况,请使用printf("%jx\n", (uintmax_t) id);
,只要pthread_t
是数字即可。 - chux - Reinstate Monica#include <iostream>
#include <pthread.h>
int main() {
pthread_t x;
printf("%li\n", (unsigned long int) x);
std::cout << (unsigned long int) x << "\n";
}
printf("%lu\n", (unsigned long int) x);
更好。为什么不直接转换为可用的最宽类型以最大化可移植性?使用 printf("%ju\n", (uintmax_t) x);
。 - chux - Reinstate Monicaint get_tid(pthread_t tid)
{
assert_fatal(sizeof(int) >= sizeof(pthread_t));
int * threadid = (int *) (void *) &tid;
return *threadid;
}
你可以这样做:
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;
}
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 OS上无法使用。
我知道这个帖子非常古老。在阅读了以上所有帖子之后,我想要提出一个更好的方法来处理这个问题: 如果你已经涉足映射业务(将pthread_to映射到一个整数),那么你可以更进一步地提高可读性。使你的pthread_create_wrapper函数接受一个字符串,即线程的名称。 我学会了在Windows和Windows CE上使用"SetThreadName()"功能。 优点:你的id不仅仅是数字,而且你还可以看到你的每个线程的作用。
如果程序启动了大量短暂的线程,查找表 (pthread_t : int)
可能会导致内存泄漏。
对 pthread_t
的字节创建哈希(无论是结构体、指针、长整型还是其他类型)可能是一种可行的解决方案,不需要使用查找表。与任何哈希一样,存在碰撞的风险,但您可以调整哈希的长度以适应您的要求。
pthread_t
值,那该怎么办? - David Schwartz
fprintf
行中对ptc
指针进行了解引用。 - cafunsigned char*
之前,将其强制转换为void*
的目的是什么?为什么第一个转换不是多余的? - fonini