Типизирование для аннулирования указателя и возврата - PullRequest
2 голосов
/ 28 сентября 2010

У меня есть функция:

void *findPos(void *param)
{
   int origPos=(int)param;
   ...
}

Который я вызываю как обработчик потока:

pthread_create( &threadIdArray[i], NULL, findPos, (void *)i );

Теперь, таким образом, я получаю значение origPos как типизированный voidуказатель param, т.е.я.Это похоже на грязный хак, чтобы обойти ограничение, заключающееся в том, что ему разрешено передавать только указатели пустоты в функцию работы с потоками.

Можно ли сделать это более чистым способом?:

Обратите внимание, что я запускаю функцию pthread_create() в цикле i for, поэтому передача указателя на i не может быть безопасным выбором.

Ответы [ 5 ]

3 голосов
/ 28 сентября 2010

Конечно: просто укажите указатель на int, как и планировал разработчик API:

void *findPos(void *param)
{
   int origPos=*(int *)param;
   ...
}

pthread_create( &threadIdArray[i], NULL, findPos, &i );

Приведение между int и void * небезопасно, поскольку преобразование не обязательно обратимо.

Вы также должны убедиться, что i все еще действует, когда поток начинает выполняться (если i имеет автоматическую продолжительность хранения, это будет, например, так, если вызывающая функция также вызывает pthread_join()).

В вашем случае (i - переменная цикла), вам следует продублировать значение переменной в безопасном месте, например, в куче через malloc() или поместив ее в стек с соответствующим временем ожидания:

static int args[THREAD_COUNT];

for(int i = 0; i < THREAD_COUNT; ++i)
{
    args[i] = i;
    pthread_create(&threadIdArray[i], NULL, findPos, args + i);
}
2 голосов
/ 28 сентября 2010

Вы должны быть уверены, что в вашей системе значение вашего параметра имеет достаточно места внутри пустого указателя (см. Тип данных intptr_t). Передача значения double может быть проблематичной с вашим "прямым" методом.

Я часто использую параметр structur для передачи значений потоковым (или другим) функциям.

struct Param {
   double foo;
   int bar;
};  

Param param;
param.foo = 1.0;
param.bar = 1;

pthread_create( &threadIdArray[i], NULL, findPos, &param );
2 голосов
/ 28 сентября 2010

Ну, вы можете передать указатель на значение или обернуть значение в структуру и передать указатель на него.Последнее само по себе не чище, но более расширяемо, если вам когда-либо понадобится более одного значения параметра int для вашего потока.

ОБНОВЛЕНИЕ:
Я использовал для предложения использованияиспользуйте intptr_t из <stdint.h>, чтобы выразить, что вы намереваетесь привести это целое число к / из void *, но чтение документации более внимательно (спасибо, Кристоф) дает:

Следующий тип обозначает целочисленный тип со знаком со свойством, что любой действительный указатель на void может быть преобразован в этот тип, затем преобразован обратно в указатель на void, и результат будет сравниваться равным исходному указателю: intptr_t

Похоже, что, как сказал Кристоф, это означает, что вы не в безопасности, если идете по этому пути, поэтому не

1 голос
/ 28 сентября 2010

Это хак, который вы не должны делать, если хотите иметь переносимый код.Во-первых, обратное преобразование из void* не обязательно четко определено, как уже сказал кто-то другой.

Но независимо от этого, это грязный хак, который идет вразрез со всеми возможными намерениями pthread_create API.Просто используйте что-то вроде этого:

size_t * threadId = calloc(n, sizeof(size_t));
for (size_t i = 0; i < n; ++i) {
   threadId[i] = i;
   ptread_create(...., &threadId[i]);
}

И у вас не будет затора на i, который был бы у вас, если бы вы передали один и тот же аргумент всем потокам.

0 голосов
/ 28 сентября 2010

Я не верю, что это действительно грязный хак. Википедия в своем примере pthreads делает то же самое.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...