#c #pthreads
#c #pthreads
Вопрос:
Функции pthread принимают аргумент void *. Как можно отправить простую структуру, а не указатель?
Я хочу отправить структуру без указателя на одну функцию pthread.
Также я хочу отправить указатель на функцию void *, как это делается? может ли какой-либо указатель быть отправлен в функцию void *?
Комментарии:
1. Чтобы передать структуру, вам нужно передать ее адрес (который преобразует его в указатель). Однако вам нужно убедиться, что объект не будет уничтожен (поскольку у потока будет ссылка на него). Да, вы можете передать любой указатель на пустоту *
2. Вы не можете (AFAIK). Что не так с указателем на структуру на основе кучи?
3. Не могу ли я просто отправить структуру, подобную этой amp; thestruct в (void *)
4. @Helium3: Да, но убедитесь, что struct не исчезает. Итак, если это была, например, локальная переменная, функция может вернуться в одном потоке, в то время как новый поток все еще имеет указатель на объект, который больше не существует…
5. @forsvarir: Если у меня есть структура для GPSLocation. У меня есть сторожевой поток, запускающий и отслеживающий поток ввода-вывода GPS и поток навигации GPS. Мне нужно передать структуру сторожевому таймеру, затем передать ее из сторожевого таймера в поток навигации. Будет ли каждая потоковая функция создавать их копию при приведении обратно из void * ? Если будет передан указатель, устранит ли это возможность потери неверных данных или структуры/
Ответ №1:
Невозможно; вы должны отправить указатель. Однако a void *
может указывать на что угодно. Если вызывается ваша переменная struct foo
, вы можете просто передать ее как (void *) amp;foo
, а внутри функции вы можете преобразовать ее обратно, например, в struct Foo
с struct Foo * fooPtr = (struct Foo *) param;
помощью struct Foo foo = *((struct Foo *) param);
или,,.
Редактировать: Как упоминалось в комментарии @forsvarir, foo
не должно быть локальной переменной (если вызывающая функция не ожидает завершения потока). Смотрите сообщение @Gavin Lock.
Ответ №2:
Основываясь на ваших комментариях, вам нужно сделать что-то вроде этого…
В вашем основном коде:
void PassSomeStuff(struct TheStruct myStruct) {
struct TheStruct *pStruct = malloc(sizeof(struct TheStruct));
memcpy(pStruct, amp;myStruct, sizeof(struct TheStruct));
/* Start the watchdog thread passing in the structure */
pthread_create(/* other args */, amp;myWatchDogThreadFunc, pStruct); */
}
В вашем сторожевом потоке:
void *myWatchDogThreadFunc(void *pArgs) {
struct TheStruct *pStruct = (struct TheStruct *)pArgs;
/* use the struct */
/* Pass Ownership to the navigation thread*/
/* Start the navigation thread passing in the structure */
pthread_create(/* other args */, amp;myNavigationThreadFunc, pStruct);
}
В вашем потоке навигации:
void *myNavigationThreadFunc(void *pArgs) {
struct TheStruct *pStruct = (struct TheStruct *)pArgs;
/* use the struct */
/* cleanup */
free(pStruct); /* or pass it to somebody else... */
}
Вы не можете просто сделать:
void PassSomeStuff(struct TheStruct myStruct) {
pthread_create(/* other args */, amp;myStruct);
}
Потому что myStruct
будут очищены… когда PassSomeStuff
возвращается. Принимая адрес (получая указатель на него), объект не копируется.
Примечание:
- ЛЮБОЙ из ваших потоков может очистить структуру, вызвав free, если вы уверены, что ВСЕ потоки завершили с этим.
- ВСЕ потоки (основной, сторожевой, навигационный) ссылаются на один и тот же экземпляр структуры (поэтому, если они изменяют ее содержимое, вам может потребоваться защитить его блокировкой). Если это не дает желаемого эффекта, то вам нужно будет создавать (malloc) новую копию структуры на каждом шаге, чтобы у каждого потока была своя собственная копия значений.
Комментарии:
1. Я собираюсь передать указатель с самого начала. обработайте их перед отправкой в watchdog (который также является потоком), а затем watchdog должен передать их в поток gps nav. Могу ли я тогда просто передать указатель вдоль аргументов void * pthread?
2. @Helium3: Я обновил свой пост, чтобы отразить то, что, по моему мнению, вы говорите, включая некоторые примечания внизу. Короткий ответ — да (при условии, что я вас правильно понял).
Ответ №3:
Как уже упоминалось, вы должны передать указатель. Думайте о void * как о нетипизированном указателе, поэтому вы должны привести его обратно к правильному типу внутри вашей потоковой функции. (см. Ответ Аасмунда)
Как упоминает forsvarir, вы должны убедиться, что указанная структура не уничтожена до того, как поток ее использует — самый безопасный способ сделать это — создать структуру в куче и передать ее адрес и владельца функции потока.
Что я имею в виду под «передачей права собственности», так это то, что функция, сообщающая о структуре, не должна удалять ее, а функция потока должна удалить структуру, как только это будет сделано с ней.
Ответ №4:
Это не полный ответ, а скорее альтернативное решение предупреждений, которые предлагали другие, о том, чтобы убедиться, что структура все еще существует, когда новый поток ее получит. Конечно, вы можете использовать malloc
для их получения и возложить ответственность за free
их обработку на новый поток. Во многих отношениях это кажется самым простым и дешевым способом (синхронизация не требуется), но на самом деле синхронизация скрыта внутри malloc
и free
и может быть немного дорогостоящей, тем более что большинство потокоориентированных распределителей (например, ptmalloc и tcmalloc) несут дополнительные расходы, когда поток, освобождающий память, не совпадает с потоком, который ее выделил.
Другой подход, который вы можете использовать, — это поместить барьер pthread внутри вашей структуры инициализации и ожидать его:
pthread_barrier_init(amp;init_struct.barrier, 0, 2);
pthread_create(amp;td, 0, start_func, amp;init_struct);
pthread_barrier_wait(amp;init_struct.barrier);
И также вызовите функцию запуска потока pthread_barrier_wait(arg->barrier);
после копирования структуры в ее собственное автоматическое хранилище.