Я пишу сервер сокетный с использованием PThreads.
После приема соединения сокет передается создаваемому потоку, который и работает с эти клиентом.
Так вот буфер где выделять?
вот так:
client_thread(..)
{
char * buf = malloc(..);
send..
...
recv..
}или выделять в главном потоке?
А какая разница ? Если тебе не нужно, чтобы к этому буферу имел доступ основной поток, то как угодно :)
Я про то, чтобы при запуске следующего потока указатель на буфер не менялся для предыдущего потока - чтоб код был типа thread-safe
Ну так если ты объявляешь переменную внутри функции, то сколько бы ты её не вызывал, в каждом экземпляре будет своя область памяти (в частности, стека) не зависимо от того, является ли функция потоком или нет. Единственное исключение - это статическая переменная (static ....) - она одна для всех, но я не уверен в том, как она будет работать в потоках.
>Ну так если ты объявляешь переменную внутри функции, то сколько бы ты
>её не вызывал, в каждом экземпляре будет своя область памяти (в
>частности, стека) не зависимо от того, является ли функция потоком или
>нет. Единственное исключение - это статическая переменная (static ....) - она
>одна для всех, но я не уверен в том, как она
>будет работать в потоках.С блокировкой будет
зафлудят твой сервер - где стока памяти возьмешь?
>зафлудят твой сервер - где стока памяти возьмешь?
Попробуйте передавать в thread дополнительно порядковый номер. Очень пригодиться при создании log. Память лучше выделять в thread. Удобно для случая, когда кол-во thread не фиксированы и Вы изменяете нагрузку.
Можно использовать tid в качестве порядковогономера номера :)
>Можно использовать tid в качестве порядковогономера номера :)К сожалению нельзя pthread_t is opaque type. Т.е. зависит от реализации
Успехов
--- sas
я экспериментик провел
void my_thread(void * arg)
{
char * p = malloc(1024);
printf("%s:p=%X",(char)arg,(int)p);
free(p);
}берем создаем 2 потока из этой ф-и и оба потока печатают одно и тоже значение p, что-то вроде
th1:p=8567DE23
th2:p=8567DE23
это как? если p - локальная переменная , то значения должны быть разные.
а чем тебе не нравится вариант приемно передающего буфера вида
char buf[maxline];
>я экспериментик провел
>void my_thread(void * arg)
>{
> char * p = malloc(1024);
> printf("%s:p=%X",(char)arg,(int)p);
> free(p);
>}
>
>берем создаем 2 потока из этой ф-и и оба потока печатают одно
>и тоже значение p, что-то вроде
>th1:p=8567DE23
>th2:p=8567DE23
>это как? если p - локальная переменная , то значения должны быть
>разные.Существуют следующие правила:
1) Все "видимые/доступные" переменные в порождающем потоке ПЕРЕД вызовом pthread_create будут "видимы/доступны" новому (порожденному) потоку После pthread_create могут не быть видны
2) Все переменные доступные потоку когда разблокируется мьютекс ( ТОЛЬКО ДО РАЗБЛОКИРОВАНИЯ) будут видимы и доступны для потока который этот мьютекс заблокирует
3) Все переменные которые видимы/доступны потоку когда он умирает (ТОЛЬКО ДО СМЕРТИ) будут видимы/доступны потоку который сделает join
4) Все что доступно потоку ДО сигнала/броадкаста будет видимо/доступно для потока который проснется по этому сигналу/броадкасту
Так что в Вашем случае все правильно согласно правилу 1.
Успехов
--- sas
спасибо
Чего-то я не вкурил.
Имеется конструкция:pthread_create(....); // поток 1
pthread_create(....); // поток 2Если я выделил память в первом потоке, потом прошло создание второго такого же потока, то второй видит данные первого ?
Тем более, если второй поток выделяет память в ту же переменную, то возможны два варианта:
(a) - адреса должны быть разными;
(б) - второй поток забьет данные первого; при этом получаем классическую утечку памяти.
Дополнительный вариант (в) - второй поток видит данные первого до тех пор, пока не сделает свой "malloc" (что-то наподобии "malloc" -> "fork", только по указанному адресу можно еще и обратиться из потомка). В этом случае возвращаемся к варианту (a).Вариант (a) может зависеть от реализации потоков. Если память выделяется не в контексте процесса, а в контексте потока, то вполне возможны 2 одинаковых адреса для разных потоков. В смысле - одинаковые адреса для потоков на самом деле являются разными адресами для процесса. Аналогия - два разных процесса могут иметь одинаковый адрес динамической памяти, но реально их данные не пересекаются.
Помоему так. Где я ошибаюсь ?
PS. Как по мне, так одинаковые адреса получаются потому, что первый поток успевает освободить память до того, как второй пытается её выделить. Интересно, если поставить, например, "sleep(5)" перед "free(p)", то какие будут результаты ?
>Чего-то я не вкурил.
>Имеется конструкция:
>
>pthread_create(....); // поток 1
>pthread_create(....); // поток 2
>
>Если я выделил память в первом потоке, потом прошло создание второго такого
>же потока, то второй видит данные первого ?НЕТ Перечитайте правило 1, согласно ему поток 0 (порождающий) и переменные видимые в потоке 0 могут быть ВИДИМЫ (И ПЕРЕДАНЫ) в потоках 1 и 2. Вот если поток 1 создаст поток 2 (pipe workflow) и передаст ему адрес своей локальной переменной, то поток 2 ее увидит. В противном случае поток 2 ничего не знает о потоке1. И стек и динамическая память (как Вы уже писали) ЛОКАЛЬНЫ для потока.
Глобальные (статические и нет) переменные видны для всех потоков без явной передачи (см пример в конце ответа)>
>Тем более, если второй поток выделяет память в ту же переменную, то
>возможны два варианта:
>
>(a) - адреса должны быть разными;Правильно, переменная - это указатель который просто получит новое значение в пределах адресного пространства процесса. Memory leak - см ниже
>(б) - второй поток забьет данные первого; при этом получаем классическую утечку
>памяти.Рассмотрим следующий пример в однопоточной программе:
char* ptr = (char*)malloc( 100 );
ptr = (char*)malloc( 50 );
free( ptr );Потеряли 100 байт от первого malloc
Или
char* ptr = strdup( "XXXXXXXXXXX" );
assert( ptr != NULL );
int l = strlen( ptr );
strncpy( ptr, "CCCCCCCCCCC", l );
ptr[ l - 1 ] = '\0';Перезаписали данные.
Отличие многопотоковой программы в том, что доступ к разделяемым данным (глобальным или переданным локальным) должен быть только внутри критических секции, иначе race condition (о dead lock сейчас не говорим).
>Дополнительный вариант (в) - второй поток видит данные первого до тех пор, пока не сделает свой "malloc" (что-то наподобии "malloc" -> "fork", только по указанному адресу можно еще и обратиться из потомка). В этом случае возвращаемся к варианту (a).
>
>Вариант (a) может зависеть от реализации потоков. Если память выделяется не в
>контексте процесса, а в контексте потока, то вполне возможны 2 одинаковых
>адреса для разных потоков. В смысле - одинаковые адреса для
>потоков на самом деле являются разными адресами для процесса. Аналогия -
>два разных процесса могут иметь одинаковый адрес динамической памяти, но реально
>их данные не пересекаются.
>
>Помоему так. Где я ошибаюсь ?
>Не думаю. Т.к. потоки принадлежат одному процессу, то и виртуальная память процесса используется ВСЕМИ потоками Это означает что адреса будут разными
При создании потока резервируется память, принадлежащая процессу, которая и используется для его (потока) стека и динамической памяти.>PS. Как по мне, так одинаковые адреса получаются потому, что первый поток
>успевает освободить память до того, как второй пытается её выделить. Интересно,
>если поставить, например, "sleep(5)" перед "free(p)", то какие будут результаты ?
>Вот пример одинаковых адресов (Только скелет кода. Могут быть ошибки и опечатки)
/*
**** thread1.c
**
** Demo for variable visibility (rule 1)
*/#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>#define S "AAAAAAAAAAAAAAAAAAAAAAAAAAA"
#define S1 "XXXXXXXXXXXXX"
#define S2 "RRRRRRRRRRRRR"pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
void fatal_err( int res, const char* txt )
{
fprintf( stderr, "FATAL ERROR: Status=%d\t%s... Exiting...", res, txt );
exit( EXIT_FAILURE );
}void* thread( void* thr_arg, const char* thr_name, const char* new_str )
{
int res;
char* sptr = (char *)thr_arg;
res = pthread_mutex_lock( &g_mutex );
if ( res != 0 )
fatal_err( res, "thread::pthread_mutex_lock failed" );fprintf( stdout, "%s:: Recieved address=%p value=[%s]\n", thr_name, sptr, sptr );
res = pthread_mutex_unlock( &g_mutex );
if ( res != 0 )
fatal_err( res, "thread::pthread_mutex_unlock failed" );/* Below completely new pointer value assigned to sptr
* thr_arg value is not affected
*/
sptr = strdup( new_str );
assert( sptr != NULL );
fprintf( stdout, "%s:: after strdup address=%p value=[%s]\n", thr_name, sptr, sptr );return (void *)sptr;
}void* test_thread_1( void* ptr )
{
return thread( ptr, "test_thread_1", S1 );
}void* test_thread_2( void* ptr )
{
return thread( ptr, "test_thread_2", S2 );
}int main( int argc, char* argv[] )
{
pthread_t thr1, thr2;
char* s1 = strdup( S );
int res;
extern int errno;
char* thr1_res = NULL, *thr2_res = NULL;if ( s1 == NULL )
fatal_err( errno, "main:: s1 allocation failed" );
fprintf( stdout, "main:: s1 address=%p value=[%s]\n", s1, s1 );res = pthread_create( &thr1, NULL, test_thread_1, (void *)s1 );
if ( res != 0 )
fatal_err( res, "main::pthread_create( test_thread_1 ) failed" );res = pthread_create( &thr2, NULL, test_thread_2, NULL );
if ( res != 0 )
fatal_err( res, "main::pthread_create( test_thread_2 ) failed" );
res = pthread_join( thr1, (void**)&thr1_res );
assert( thr1_res != NULL );res = pthread_join( thr2, (void**)&thr2_res );
assert( thr1_res != NULL );fprintf( stdout, "main:: s1 after join address=%p value=[%s]\n", s1, s1 );
fprintf( stdout, "main:: thr1_res after join address=%p value=[%s]\n", thr1_res, thr1_res );
fprintf( stdout, "main:: thr2_res after join address=%p value=[%s]\n", thr2_res, thr2_res );
free( s1 );
free( thr1_res );
free( thr2_res );return 0;
}
Когда я говорю о динамической памяти потока я имею в виду, что переменные обеспечивающие доступ к динамической памяти локальны для потока Содержимое динамической памяти принадлежит процессу. Именно поэтому даже когда поток завершился мы можем использовать данные из heapУспехов
--- sas
Это-то я всё понял. Я только не понял, каким образом "правило 1" объясняет совпадение адресов в разных потоках, при условии, что каждый из них выделяет память для локальной переменной :) Ведь кусок кода был такой:char * p = malloc(1024);
printf("%s:p=%X",(char)arg,(int)p);
free(p);То есть, вывод адреса на экран происходил уже после того, как локальная переменная потока получила его от "malloc()". Я потому в "PS" сове представление происходящего и написал :)
>Это-то я всё понял. Я только не понял, каким образом "правило 1"
>объясняет совпадение адресов в разных потоках, при условии, что каждый из
>них выделяет память для локальной переменной :) Ведь кусок кода был
>такой:
>
>char * p = malloc(1024);
>printf("%s:p=%X",(char)arg,(int)p);
>free(p);
>
>То есть, вывод адреса на экран происходил уже после того, как локальная
>переменная потока получила его от "malloc()". Я потому в "PS" сове
>представление происходящего и написал :)Вопрос был:
========================
я экспериментик провел
void my_thread(void * arg)
{
char * p = malloc(1024);
printf("%s:p=%X",(char)arg,(int)p);
free(p);
}берем создаем 2 потока из этой ф-и и оба потока печатают одно и тоже значение p, что-то вроде
th1:p=8567DE23
th2:p=8567DE23
это как? если p - локальная переменная , то значения должны быть разные.
=========================Ключевая фраза:
"берем создаем 2 потока из этой ф-и и оба потока печатают одно и тоже значение p"
:)
Как я понял что-то вроде
void my_thread(void * arg)
{
char * p = malloc(1024);
printf("%s:p=%X",(char)arg,(int)p);
pthread_create( &thr, NULL, thread1, (void*)p );
pthread_create( &thr, NULL, thread2, (void*)p );
/***free(p);***/
}Если автор имел в виду создание 2-х потоков НЕ ИЗ этой, а с использованием этой функции:
pthread_create( &thr, NULL, my_thread, NULL );
pthread_create( &thr, NULL, my_thread, NULL );то Вы были правы. Из-за использования уже освобожденной памяти (1-ый вызов my_thread уже сделал free) один и тот же адрес может быть присвоен двум локальным переменным в разных потоках. При этом они (переменные потоков) одновременно не живут
void my_thread(void * arg)
{
char * p = malloc(1024);
printf("%s:p=%X",(char)arg,(int)p);
sleep( 10 );
free(p);
}и скорее всего адреса будут разными
Прошу прощения за разночтение :(
Успехов
--- sas
попробуйте сделать так:printf("%s:p=%X",(char*)arg,(int)p);
p.s.1. malloc() отводит блок памяти в куче, что не относится к thread'ам никаким образом (куча по умолчанию для всех одна). к thread'ам относится только указатель на блок памяти char* p, который уникален для каждого thread'а, в силу того, что функция my_thread() выполняется параллельно, а это локальная переменная.
2. старайтесь избегать глобальных переменных, т.к. их использование увеличивает вероятность искажения (несанкционированного изменения) данных. а если этого не получается избежать полностью -- сведите их количество до одного экземпляра, создав класс/модуль, который скрывает эти данные за операциями доступа. это особенно актуально для паралельных программ. почему -- попробуйте ответить сами.
3. старайтесь проводить согласованную политику по захвату/освобождению блоков памяти в куче. согласованная -- значит код, выполняющий эти действия, должен быть локализован (инкапсулирован) в одном классе/блоке/модуле. и это особенно актуально для паралельных программ. почему -- также на вашей совести ответ.
Буфер вынес в tcb(описал структуру и для каждого потока выделяю отдельную структурную переменную).
Видите ли, если ставить мутекс_локи или заставлять один поток спать, то значения указателя у каждого потока свое, одного не пойму - если это локальная переменная потока, то почему один поток интерферирует с другим -стеки-то у них по идее разные?
> одного не пойму - если это локальная переменная потока, то почему один поток интерферирует с другим -стеки-то у них по идее разные?Посмотри соседнюю ветку - там объяснение есть :)