Вход/Регистрация
UNIX: разработка сетевых приложений
вернуться

Стивенс Уильям Ричард

Шрифт:

}

С точки зрения ANSI С здесь все в порядке: мы гарантированно можем преобразовать целочисленный указатель к типу

void*
и затем обратно преобразовать получившийся указатель на неопределенный тип к целочисленному указателю. Проблема заключается в другом — на что именно он будет указывать?

В главном потоке имеется одна целочисленная переменная

connfd
, и при каждом вызове функции
accept
значение этой переменной меняется на новое (в соответствии с новым присоединенным сокетом). Может сложиться следующая ситуация:

Функция

accept
возвращает управление, записывается новое значение переменной
connfd
(допустим, новый дескриптор равен 5) и в главном потоке вызывается функция
pthread_create
. Указатель на
connfd
(а не фактическое его значение!) является последним аргументом функции
pthread_create
.

Создается новый поток, и начинает выполняться функция

doit
.

Готово другое соединение, и главный поток снова начинает выполняться (прежде, чем начнется выполнение вновь созданного потока). Завершается функция

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

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

connfd
, которое, согласно нашему предположению, равно 6. Проблема заключается в том, что несколько потоков получают доступ к совместно используемой переменной (целочисленному значению, хранящемуся в
connfd
) при отсутствии синхронизации. В листинге 26.2 мы решаем эту проблему, передавая значение переменной
connfd
функции
pthread_create
, вместо того чтобы передавать указатель на это значение. Этот метод работает благодаря тому способу, которым целочисленные значения в С передаются вызываемой функции (копия значения помещается в стек вызванной функции).

В листинге 26.3 показано более удачное решение описанной проблемы.

Листинг 26.3. Эхо-сервер TCP, использующий потоки с более переносимой передачей аргументов

//threads/tcpserv02.c

1 #include "unpthread.h"

2 static void *doit(void*); /* каждый поток выполняет эту функцию */

3 int

4 main(int argc, char **argv)

5 {

6 int listenfd, *iptr;

7 thread_t tid;

8 socklen_t addrlen, len;

9 struct sockaddr *cliaddr;

10 if (argc == 2)

11 listenfd = Tcp_listen(NULL, argv[1], &addrlen);

12 else if (argc == 3)

13 listenfd = Tcp_listen(argv[1], argv[2], &addrlen);

14 else

15 err_quit("usage: tcpserv01 [ <host> ] <service or port>");

16 cliaddr = Malloc(addrlen);

17 for (;;) {

18 len = addrlen;

19 iptr = Malloc(sizeof(int));

20 *iptr = Accept(listenfd, cliaddr, &len);

21 Pthread_create(&tid, NULL, &doit, iptr);

22 }

23 }

24 static void*

25 doit(void *arg)

26 {

27 int connfd;

28 connfd = *((int*)arg);

29 free(arg);

30 Pthread_detach(pthread_self);

31 str_echo(connfd); /* та же функция, что и раньше */

32 Close(connfd); /* мы закончили с присоединенным сокетом */

33 return (NULL);

34 }

17-22
Каждый раз перед вызовом функции
accept
мы вызываем функцию
malloc
и выделяем в памяти пространство для целочисленной переменной (дескриптора присоединенного сокета). Таким образом каждый поток получает свою собственную копию этого дескриптора.

28-29
Поток получает значение дескриптора присоединенного сокета, а затем освобождает занимаемую им память с помощью функции
free
.

  • Читать дальше
  • 1
  • ...
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: