LINUX.ORG.RU

pthreads help


0

0

Добрый вечер всем программистам.
Помогите советом.
Есть программа:

#define LOGFILE "/tmp/logfifo"
void* safe_delimit(void* string)
{
char *arg_pointer = (char*)string;

if(!arg_pointer){
printf("safe_delimit(): нулевой указатель string");
return;}
printf("stream: %s\n", arg_pointer);
return;
}

int main()
{
FILE* logfile;
char buffer[1024] = "";
if((logfile = fopen(LOGFILE, "r")) == (FILE *)NULL) {
printf("ошибка открытия файла %s", LOGFILE);
return -1;}
while(fgets(buffer, sizeof(buffer), logfile) != (char*) NULL) {
pthread_t child;
printf("%s", buffer);
if(pthread_create(&child, NULL, safe_delimit, buffer) != 0)
printf("ошибка создания потока\n");
else
pthread_join(child, 0); }
fclose(logfile);
return 0;
}

LOGFILE здесь является fifo файлом. Другой процесс в него постоянно пишет строки, разделенные '\n', а эта програма их читает и на каждую строку создает отдельный поток.
Если ставлю pthread_join(child, 0); , то все отлично, а если pthread_detach(child); , то начинается какая-то ерунда: когда строка считается, на нее то не создается потока, то создается сразу два потока, а то поток создается, но параметр пустой. Как я понимаю, pthread_join(child, 0); заставляет программу ждать завершения потока, а мне нужно что-бы все потоки и родительская программа были независимы.
Почему такое разное поведение при pthread_join(child, 0); и pthread_detach(child);?
Спасибо за люьые советы.

anonymous

непонятно, на что вы вообще надеетесь с этим кодом.

во первых, если уж создавать поток на строку, то эту
строку надо копировать, и эту копию передавать потоку
как параметр. а у вас все потоки работают с одной областью
памяти, которая непрерывно и асинхронно меняется в main().

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

> то создается сразу два потока

это вы как определили?

> то поток создается, но параметр пустой.

это что означает?

idle ★★★★★
()

а зачем тебе join???

делай себе нить, пускай в свободное плавание, и хай живет. Если будешь много нитей создавать/убивать, то не забудь на каждую созданную выставить опцию - очищать память сразу после смерти - иначе больше чем ~1024 нитей сделать не сможешь, даже если все предыдущие уже мертвые.

Для синхронизации с материнским/другими потоком юзай atomic_t - atomic_set() etc...

И будет тебе щастье. Для простоя нитей можно использовать sched_yield().

Если честно, то с pthread_join я не смог разобраться, не совсем подходило мне - все, что выяснил на практике, написал выше :)

Удачи в таком классном деле!

Spectr ★★★
()

За что мне нравится linux.org.ru, так это за раздел DEVELOPMENT. Тут всегда корректные, желающие помочь люди. Я с нитями (потоками) столкнулся впервые и, поэтому, не судите строго за незнание.

>>to idle:

>>далее. я полагаю, что процессоров на вашей машине меньше,
>>чем строк в файле.,
Да, действительно, у меня однопроцессорный pentium 3 800 МГц.

>>поэтому не удивительно, что pthread_create()
>>будет возвращать ошибку
У меня pthread_create() ошибок не возвращает (в коде видно, что стоит проверка на ошибку) а ведет себя как будто он успешно создался.

>>т.к. вы плодите потоки, которые
>>не успевают завершаться (и даже получить cpu), а количество
>>процессов/потоков, которое можно создать не бесконечно.
можно-ли здесь подробнее как сделать правильно, а то я сам об этом пока информации не нашел.

>> то создается сразу два потока
>> это вы как определили?
через printf("%s", buffer); в main и printf("stream: %s\n", arg_pointer); в safe_delimit. Приняв строку, она печатается в main и в создавшемся потоке. То есть на один printf("%s", buffer); в main выпадает два printf("stream: %s\n", arg_pointer); в safe_delimit.

>> то поток создается, но параметр пустой.
>>это что означает?
На один printf("%s", buffer); в main выходит один stream: а самой строки нет.

>> to Spectr:

>> а зачем тебе join???
>> делай себе нить, пускай в свободное плавание, и хай живет.
Мне join как раз не нужен. Просто моя программа работает только в варианте с join. Мне как раз и нужно пускать нить в свободное плавание, что бы main тутже продолжала обрабатывать новые приходящие строки, а не ждала чьего-то завершения.

>> Если будешь много нитей создавать/убивать, то не забудь на каждую
>> созданную выставить опцию - очищать память сразу после смерти - иначе >> больше чем ~1024 нитей сделать не сможешь, даже если все предыдущие >> уже мертвые. Для синхронизации с материнским/другими потоком юзай
>> atomic_t - atomic_set() etc... И будет тебе щастье. Для простоя нитей можно >> использовать sched_yield().
Можно ли подробнее как правильно все это делать или ссылку где про это почитать?

Я сначала привел немного не тот код, что у меня. Я не указал, что у меня в цикле while строка проверяется на совпадение с "NOT DIRECT":

#define LOGFILE "/tmp/logfifo"
void* safe_delimit(void* string)
{
char *arg_pointer = (char*)string;

if(!arg_pointer){
printf("safe_delimit(): нулевой указатель string");
return;}
printf("stream: %s\n", arg_pointer);
return;
}

int main()
{
FILE* logfile;
char file_string[1024] = "";
if((logfile = fopen(LOGFILE, "r")) == (FILE *)NULL) {
printf("ошибка открытия файла %s", LOGFILE);
return -1;}

while(fgets(file_string, sizeof(file_string), logfile) != (char*) NULL) {
pthread_t child;
char buffer[1024] = "";

strncpy(buffer, file_string, sizeof(buffer)-1);
buffer[sizeof(buffer)-1] = '\0';
printf("%s", buffer);
if(strcmp(buffer, "NOT DIRECT") != 0)
{
if(pthread_create(&child, NULL, safe_delimit, buffer) != 0)
printf("ошибка создания потока\n");
else
pthread_detach(child); }
}
fclose(logfile);
return 0;
}

Сначала buffer был как в примере в вопросе. Потом я сделал копию строки buffer из строки file_string как сказал idle. Но это ничего не дало. Потом я перенес проверку "NOT DIRECT" в тело потока, а из цикла убрал и ВСЕ ЗАРАБОТАЛО. Как это вообще можно обьяснить?

anonymous
()
Ответ на: комментарий от anonymous

Только один совет: память для buffer выделяй динамически и освобождай в потоке.
В противном случае у тебя возможна ситуация когда два потока будут работать с одним и тем же буфером.
Или буфер будет изменён в цикле основной программы до сброса потоком в файл.

romanSA
()
Ответ на: комментарий от romanSA

На мой взгляд, проблема с массивом buffer в том, что он автоматически освобождается при выходе из тела цикла (здесь кончается его область видимости). Поэтому когда поток не отсоединен (не сделан pthread_detach()) основной поток останавливается, т.е. не успевает завершить очередной проход тела цикла и с buffer все нормально в таком варианте. Когда же поток отсоединен, то он может начать исполняться когда основной поток завершит проход тела цикла и заблокируется на ожидании следующей строки,а что там в buffer находится в таком случае вообще точно не понятно. Поэтому, как уже было написано выше, память под копию принятой строки надо выделять динамически через вызов malloc или что-то подобное. Кроме того не совсем понятно зачем используется вызов strncpy вместо strcpy.

AVI
()
Ответ на: комментарий от AVI

Ура. Все заработало. Дело действительно было в malloc.
>> to AVI
>> зачем используется вызов strncpy вместо strcpy
Привычка проверять длину записываемых данных. Даже не обратил внимание, что длина контролируется fgets().

Ребята!!! Огромнейшее Вам спасибо.

anonymous
()
Ответ на: комментарий от anonymous

> > > то создается сразу два потока
> > это вы как определили?
>
> То есть на один printf("%s", buffer); в main выпадает
> два printf("stream: %s\n", arg_pointer); в safe_delimit.

> > > то поток создается, но параметр пустой.
> > это что означает?
> 
> На один printf("%s", buffer); в main выходит один stream:
> а самой строки нет.

давайте такой псевдокод рассмотрим:

thread(int* arg)
{
	printf("thread: %d.\n", arg*);
}

int main(void)
{
	int count;

	for (count = 0;; ++count)
	{
		printf("main: %d.\n", count);

		pthread_create(&child, NULL, thread, &count);
	}
}

мы можем увидеть что угодно в выводе, например:

main: 0.
main: 1.	// это не значит, что поток не создался. он создан, и ждет,
		// когда планировщик даст ему cpu
main: 2.
thread: 2.
thread: 2.
thread: 2.	// это не значит, что создалось три потока. это выполняется
		// сначала первый созданный поток, потом 2-ой и 3-ий. но все
		// они печатают 2, т.к. переменная одна (одна область памяти),
		// и к моменту выполнения уже изменилась в main().

теперь понятно?

> Потом я сделал копию строки buffer из строки file_string как сказал idle.

вы _не_ сделали копию. эта копия по прежнему одна на все потоки и main.

> ВСЕ ЗАРАБОТАЛО. Как это вообще можно обьяснить? 

наверняка не заработала. если вы запостите отформатированный код, можно
будет посмотреть.

idle ★★★★★
()
Ответ на: комментарий от idle

Согласен, malloc() в данном случае не спасает, он только уменьшает вероятность сбоя.

Пожалуй нужно испльзовать mutex для разруливания этой ситуации. То есть пока запущенный тред не скопировал переданные ему данные, родитель должен ждать.

P.S. Кстати, научи меня тупого, Как тут на форуме правильно форматировать код? Сколько не пытался так не получается.

Dead ★★★★
()
Ответ на: комментарий от Dead

Dead wrote:
> Кстати, научи меня тупого, Как тут на форуме
> правильно форматировать код? Сколько не пытался
> так не получается.

ага! значит, не один я тупой :) я тоже много раз пытался,
потом таки выяснилось, что выбор 'Preformatted text' над
кнопкой 'Post/Поместить' делает то, что нужно.

malloc() его может и спасет, если делать правиьно. когда
я писал, последнего его ответа с упоминанием malloc() еще
не было, я говорил про код, в котором он копирует из одного
локального буфера в другой локальный.

main() должен делать что-то вроде strdup() и передавать
потоку, он должен будет сделать free(). тогда не надо
mutex. но это все равно плохо, и непонятно зачем.
если он рассчитывает, что будет быстрее, то ошибается.

idle ★★★★★
()
Ответ на: комментарий от idle

Еще раз извиняюсь за незнание предмета, потому как новичок.
2idle:
>>main() должен делать что-то вроде strdup() и передавать
>>потоку, он должен будет сделать free().
А разве 'malloc() strcpy' и 'strdup()' не одно и то же по смыслу? Strdup() дублирует строку в выделенную им память, которую потом нужно освобождать, а 'malloc() strcpy()' делают то же самое вручную.

Все дело в том, что у меня функция потока safe_delimit() на самом деле очень большая. Там и операции по проверке пришедшей строки, и ее разбор на параметры, и активная работа с базой данных и работа с блокировками. Если бы это был не поток, а обычная функция, то вся программа, приняв одну строку из fifo-файла, ожидала бы окончания выполнения функции safe_delimit(). А так я пытаюсь сделать, что бы прием строк из fifo файла не простаивал, на каждую строку создавался бы отдельный независимый поток, а прием строк шел дальше. Кстати в варианте с malloc() у меня сначала работало нормально, а когда я задал огромную нагрузку и строки из fifo посыпались в несколько раз быстрее, то случился segmentation fault. Я думаю, что такие вещи, как создание процессов и потоков, нельзя делать из под цикла или надо делать какую-то задержку. 

Отсюда вопрос. Как будет вести себя канал fifo, если процесс записи в канал будет идти очень интенсивно, а процесс чтения нет? будут ли строки сохраняться в каком-то буфере или не будет происходить записи в канал если в этот момент не происходит чтения из канала другой программой? В любом случае, как мне кажется, необходимо все время считывать из канала данные без задержек. Отсюда и попытка наладить разделение работы на потоки. А потоки выбраны потому, что работа всех потоков идет с одним глобальным линейным списком, который я для краткости в примере не показал.
Мне интересно, видел ли кто-гибудь в каких-то программах порождение потоков или процессов из цикла и, если видел, то как там это реализовано?
В моем варианте с потоками в чем может быть причина segmentation fault? и каким способом возможно организовать задержку при большом потоке строк из fifo?

С уважением, автор вопроса.

anonymous
()
Ответ на: комментарий от anonymous

> > main() должен делать что-то вроде strdup() и передавать
> > потоку, он должен будет сделать free().
> А разве 'malloc() strcpy' и 'strdup()' не одно и то же

одно и тоже, но я же не видел вашего кода.

> пытаюсь сделать, что бы прием строк из fifo файла не простаивал, на каждую
> строку создавался бы отдельный независимый поток, а прием строк шел дальше.

и вы полагаете, что так будет быстрее? не забывайте, что количество одновременно
выполняющихся потоков ограниченно количеством процессоров. вы можете что-то
выиграть на блокирующих операциях, но не забывайте что потоки не бесплатны.

> Кстати в варианте с malloc() у меня сначала работало нормально, а когда я
> задал огромную нагрузку и строки из fifo посыпались в несколько раз быстрее,
> то случился segmentation fault. Я думаю, что такие вещи, как создание
> процессов и потоков, нельзя делать из под цикла или надо делать какую-то

вот в самом первом ответе я про это и писал. нельзя бесконтрольно
плодить процессы.

> или не будет происходить записи в канал если в этот момент не происходит
> чтения из канала другой программой?

да, после того как pipe заполнится, его размер 4k.

вам следует заранее создать пул потоков, и не плодить их динамически.
если максимальный размер строки небольшой (скажем, 100 байт), то проще
всего писать в pipe все время по 100 байт, и все потоки будут сами
из него считывать те же 100 байт независимо. иначе все будет гораздо
сложнее, понадобится condvar или что-то вроде этого.

однако далеко не факт, что вам вообще нужно связываться с потоками.

P.S. ОЧЕНЬ трудно читать ваши посты, они уползают вправо на много
страниц. пожалуйста, форматируйте.

idle ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.