那么我可以通过 pthread_create 将父堆栈上某些东西的地址传递给子线程并期望它能够工作吗?
仅当您确保父堆栈上的内容在子线程可能访问它的范围内时。
例如,假设您有一个函数,它使用固定数量的线程来执行某些任务。您可以在父堆栈上存储一个结构来描述每个线程的工作,例如,如果父在线程的生命周期内保持在同一范围(例如函数)中。
这听起来可能很简单,但它很容易产生错误(有点类似于 use-after-free,除了这里 free 部分有引用的变量或对象传递出去范围)。
一般来说,动态分配描述工作的结构,分别为每个线程分配,而不是使用局部变量(数组)要容易得多。它可以让你做类似的事情
struct thread_work {
struct thread_work *next; /* Singly linked list */
pthread_t id; /* Thread ID */
/* Threads may NOT change the above, shouldn't even access them */
/* Stuff that describes the work each thread should do */
};
void cancel_threads(struct thread_work *list)
{
while (list) {
struct thread_work *curr = list;
list = list->next;
curr->next = NULL;
if (!pthread_cancel(curr->id))
pthread_join(curr->id, NULL);
free(curr);
}
}
struct work_item *create_threads(void *(*worker)(struct work_item *),
size_t count,
size_t stacksize)
{
struct work_item *list = NULL;
struct work_item *curr;
pthread_attr_t attrs;
int result;
pthread_attr_init(&attrs);
if (stacksize > 0)
pthread_attr_setstacksize(&attrs, stacksize);
while (n-->0) {
curr = malloc(sizeof *curr);
if (!curr) {
cancel_threads(list);
pthread_attr_destroy(&attrs);
errno = ENOMEM;
return NULL;
}
/* TODO: Set up work-specific fields in curr */
/* Chain items into an easily managed linked list */
curr->next = list;
list = curr;
/* Create the worker thread */
result = pthread_create(&(curr->id), &attrs,
(void *(*)(void *))worker,
curr);
if (result) {
cancel_threads(list);
pthread_attr_destroy(&attrs);
errno = result;
return NULL;
}
}
pthread_attr_destroy(&attrs);
errno = 0;
return list;
}
一般来说,很少有合理的理由使用本地(堆栈)变量而不是动态分配的变量作为线程参数。
子线程能看到父进程的栈吗?
进程是线程所属的实体。你所说的“父进程”只是初始线程。在 POSIX (pthreads) 中,初始线程除了最初是进程中唯一的一个之外没有其他特殊属性。
线程可以看到属于进程的所有内存,包括堆栈区域。但是,它很少有用,因为访问属于另一个线程的堆栈的线程只有在您确保(使用例如互斥锁)将检查其堆栈的线程在适当的范围内(运行特定的函数或代码)时才有意义.
一般来说,您应该将堆栈视为用于内部簿记的内存搅动区域,其中局部变量仅在其作用域内的短时间内存在;因此完全不适合跨线程数据共享。
子堆栈是否在创建时填充了父堆栈的副本?
没有。也就是说,您不能假设会发生这种情况。
(虽然某些架构或操作系统可能会这样做,但我不知道当前有任何支持 pthreads 的架构或操作系统实际上这样做。)