一个非常不言自明的问题。例如,pthread_create的头显示它使用一个指向线程的指针:
int WINPTHREAD_API pthread_create(pthread_t *th, const pthread_attr_t *attr, void *(* func)(void *), void *arg);
好吧,有意义的是,您在内存中分配一个pthread,然后将指针传递给pthread_create,这样它就可以初始化了。。。但现在来看pthread_join:的标题
int WINPTHREAD_API pthread_join(pthread_t t, void **res);
它需要pthread_t的副本。我只是不明白为什么它不把指针指向已经存在的线程,而不是复制并传递它;这样做似乎会导致更多的问题和内存使用。我是不是错过了什么?我读了手册页,它似乎没有提供这样做的理由。
它需要
pthread_t
的副本。
是。
我只是不明白为什么不需要指向已经存在的线程的指针
pthread_t
是线程标识符。规格一直都是这样说的。复制它不会复制线程本身,也不会消耗超过一个pthread_t
占用的内存。
而不是复制它把它传过去;如果有什么不同的话,这样做会引起更多问题和更多的内存使用。
它不一定会导致更多的内存使用,因为pthread_t
不一定比指针大。它可能是指针或整数。然而,即使它是一个结构,也没有理由认为它太大以至于按值传递它会带来重大问题,因为细节由pthreads实现控制。为什么实施者会那样开枪打自己的脚?请注意,按值传递结构本身并不比传递指针效率低。
至于除了过度使用内存之外的问题,您必须更加具体,但我认为直接访问线程标识符的副本没有任何固有的问题vs。为了那些按值接受pthread_t
的函数的目的而间接地访问公共标识符对象。
我遗漏了什么吗?
我怀疑您的担忧与对类型pthread_t
的误解有关,CCD_6以某种方式承载支持线程操作的数据,而不是简单地标识线程。
您可能还认为pthreads是一个具有特定实现的库,而事实上,它首先是一个规范,旨在提供多个实现。这也是定义抽象数据类型pthread_t
而不是指定int
或struct something *
的部分原因——实现可以选择要使用的实际类型。
也许您也过于关注API函数了。即使在某些特定的实现中,按值向pthread_join()
传递pthread_t
的效率不如向一个值传递指针的效率,你认为这会产生多大的影响?pthread_join()
很少被调用,并且仅在调用者准备阻塞的情况下被调用。如果参数传递比其他情况下多消耗几纳秒,那又有什么关系呢?
我阅读了曼佩奇,这似乎没有提供任何理由。
很少有手册页提供功能设计的基本原理,但我认为最有可能的解释是形式遵循功能。那些按值接收pthread_t
的函数之所以这样做,是因为它们不需要或不想修改调用者的值。功能的设计反映了这一点。
pthread_t
是一个小对象,用于标识线程。它可以是一个指针、整数,也可能是一个微小的结构。pthread_t
实际上并不是线程本身,就像Win32中的HWND
对象是窗口本身一样。
pthread_create
函数通过指针指针返回该标识符,因为它已经返回了用于错误指示的int
类型的值。其他函数按值取pthread_t
。
例如,要比较两个pthread_t
对象是否引用同一个线程,应该使用pthread_equal
,它接受两个CCD22参数。这个函数所做的可能只是使用==
来比较这两个值,但直接这样做不那么容易移植。如果pthread_t
恰好是一个小结构,它甚至不会编译。
需要注意的一点是pthread是一个C API。与具有构造函数的C++不同,复制C变量可以保证相当便宜。最坏的情况是一个大型结构的类型,但即便如此,它也只是一个memcpy。这与C++不同,在C++中,副本可以调用任意昂贵的构造函数。