Функция pthread_create
может вызывать функции только со следующей подписью:
void *fn(void *)
Даже если вам удалось привести указатель на функцию с другой подписью и успешно передать ее pthread_create
, ваша программа может аварийно завершить работу, поскольку pthread_create
попытается настроить стек / регистры таким образом, чтобы это соответствовало соглашению о вызовах платформы для функции с одним аргументом void *
, что приведет к тому, что ваша функция будет внеопределенное состояние.
Способ решения вашей проблемы заключается в использовании функции-обертки, специально предназначенной для вызова pthread_create
, например:
void *main_original_start_routine(void *arg)
{
main_original(argc, argv);
return NULL;
}
Однако это может бытьдостаточно, если argc
и argv
не являются глобальными переменными.Вы можете обнаружить, что вам также необходимо каким-то образом передать эти значения этой функции из области, в которой вы вызываете pthread_create
.Это можно сделать с помощью аргумента void *arg
для pthread_create
, создав структуру, содержащую нужное вам состояние, и передав ее через приведенный указатель void:
struct main_original_context {
int argc;
char **argv;
};
void *main_original_start_routine(void *arg)
{
/* Convert the void pointer back to the struct pointer it
* really is. */
struct main_original_context *ctx = arg;
main_original(ctx->argc, ctx->argv);
return NULL;
}
int main(int argc, char **argv)
{
pthread_t t;
struct main_original_context ctx = {
argc,
argv
};
/* Pass a pointer to our context struct to the thread routine. */
pthread_create(&t, NULL, &main_original_start_routine, &ctx);
pthread_join(&t, NULL);
return 0;
}
Однако имейте в виду, чтоВ этом случае ctx
имеет только время жизни функции main
.Если функция, в которой мы создаем pthread, не присоединяется к pthread_join
перед возвратом (и делает недействительной структуру, используемую для предоставления контекста потоку), то это будет небезопасно.Поэтому нам пришлось бы использовать динамическое распределение и заставить поток взять на себя ответственность за освобождение любой динамически распределенной памяти:
struct main_original_context {
int foo;
int bar;
};
void *foobar_start_routine(void *arg)
{
struct main_original_context *ctx = arg;
foobar(ctx->foo, ctx->bar);
/* Free memory we have been given responsibility for. */
free(ctx);
return NULL;
}
void asdf(int foo, int bar)
{
pthread_t t;
struct main_original_context *ctx;
/* Allocate memory. */
ctx = malloc(sizeof *ctx);
ctx->foo = foo;
ctx->bar = bar;
/* Assume `main_original_start_routine` is now responsible for freeing
* `ctx`. */
pthread_create(&t, NULL, &foobar_start_routine, ctx);
/* Now we can safely leave this scope without `ctx` being lost. In
* the real world, `t` should still be joined somewhere, or
* explicitly created as a "detached" thread. */
}