[英]C - struct with a function pointer, how to fix “variable number of arguments” warning?
[英]Pointer to args struct changing too quickly, so thread does not receive correct arguments. How do I fix this?
基本上,在第一个线程有机会访问该结构并读取它接收到的信息之前,我的 args 结构的参数发生了变化。 因此对于下面的代码,第一个线程仍然将args.length
读取为 10,而不是像它本来的那样读取 5。 我知道这是因为当我在第一个thread_create()
) 之后添加sleep(1)
时。
pthread_t thread1, thread2;
struct _args args;
void *args_ptr = &args;
args.length = 5;
pthread_create(&thread1, NULL, array_add, args_ptr);
args.length = 10;
pthread_create(&thread2, NULL, array_add, args_ptr);
我可以使用互斥体来解决这个问题吗?
在我的程序中,我实际上是在 for 循环中创建线程。 因此,两个线程都创建调用,需要接收相同的args_ptr
; 我可以尝试为每个 thread_create 调用创建一个新的 args 结构,但这感觉很奇怪。 有什么建议吗?
您不应该将指向本地对象的指针传递给另一个线程:object 可能确实在另一个线程有机会访问它之前更改或被丢弃。
您应该为每个新线程使用具有适当生命周期的单独_args
结构。 一个专用的 static object 或一个你分配并传递一个指针给它,让线程从中读取它的 arguments 并在不再需要时释放:
pthread_t thread1, thread2;
static struct _args args1, args2;
args1.length = 5;
pthread_create(&thread1, NULL, array_add, &args1);
args2.length = 10;
pthread_create(&thread2, NULL, array_add, &args2);
或者
pthread_t thread1, thread2;
struct _args *args;
args = calloc(1, sizeof(*args));
args->length = 5;
pthread_create(&thread1, NULL, array_add, (void *)args);
args = calloc(1, sizeof(*args));
args->length = 10;
pthread_create(&thread2, NULL, array_add, (void *)args);
但是请注意,如果没有适当的锁定机制, array_add
线程不应同时访问同一个数组。 否则行为将是未定义的。
如果您通过此_args
结构传递的唯一参数是 integer length
,您可以将此值转换为(void *)(uintptr_t)
并在线程 function 中转换回(int)(uintptr_t)
:
#include <stdint.h>
#include <pthread.h>
void *array_add(void *arg) {
int length = (int)(uintptr_t)arg;
[...]
}
int main() {
[...]
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, array_add, (void *)(uintptr_t)5);
pthread_create(&thread2, NULL, array_add, (void *)(uintptr_t)10);
[...]
}
基本上,在第一个线程有机会访问该结构并读取它接收到的信息之前,我的 args 结构的参数发生了变化。
[...]
我可以使用互斥体来解决这个问题吗?
不直接或容易。 您可以使用线程交互管理的瑞士军刀,一个条件变量,但是对于一次性暂停以允许每个新线程在主线程继续之前到达某个点,我会改用信号量。 稍后会详细介绍。
但是,首先,您还说。
在我的程序中,我实际上是在 for 循环中创建线程。 因此,两个线程都创建调用,需要接收相同的
args_ptr
;
仅当线程打算通过该 object 彼此共享数据时,您才需要多个线程使用指向同一struct _args
object 的指针。您命名和使用它的方式表明,您只是想传达(可能不同)从初始线程到每个新线程的一次性信息。 你不需要相同的struct _args
。 您可以创建一个数组,以确保它比所有线程都长的方式和位置,并为每个线程提供自己的数组。 或者,您可以为每个线程动态分配一个新线程,以便再次为每个线程分配自己的线程。
我可以尝试为每个 thread_create 调用创建一个新的 args 结构,但这感觉很奇怪。
为什么? 如果您为每个线程提供逻辑上独立的数据,那么为什么通过不同的对象来传送它会很奇怪? 请注意,这是标准 C 值传递语义的一个很好的模拟。
但是假设你真的想为每个线程使用相同的 args object,而不是主线程在每个线程读取它之前继续进行,我提到的基于信号量的解决方案看起来像这样:
// ...
sem_t thread_start_sem;
#define ABORT_IF_NZ(x, msg) do { \
if ((x) != 0) { \
perror(msg); \
abort(); \
} \
} while (0)
void *thread_func(void *args) {
// make a local copy of the argument data
struct _args my_args = *(struct _args *) args;
// Allow the main thread to proceed
ABORT_IF_NZ(sem_post(&thread_start_sem), "sem_post");
// ... args is never accessed again ...
}
int main() {
ABORT_IF_NZ(sem_init(&thread_start_sem, 0, 0), "sem_init");
pthread_t thread1, thread2;
struct _args args;
args.length = 5;
errno = pthread_create(&thread1, NULL, thread_func, &args);
ABORT_IF_NZ(errno, "pthread_create");
ABORT_IF_NZ(sem_wait(&thread_start_sem), "sem_wait");
args.length = 10;
errno = pthread_create(&thread2, NULL, thread_func, &args);
ABORT_IF_NZ(errno, "pthread_create");
ABORT_IF_NZ(sem_wait(&thread_start_sem), "sem_wait");
// ...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.