Добавить в цитаты Настройки чтения

Страница 284 из 399

  co

  Pthread_create(&tid, NULL, &doit, &co

 }

}

static void* doit(void *arg) {

 int co

 co

 Pthread_detach(pthread_self());

 str_echo(co

 Close(co

 return(NULL);

}

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

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

■ Функция accept возвращает управление, записывается новое значение переменной co

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

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

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

В листинге 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 co

28  co

29  free(arg);

30  Pthread_detach(pthread_self());

31  str_echo(co

32  Close(co

33  return (NULL);

34 }

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

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

Исторически функции malloc и free не допускали повторного вхождения. Это означает, что при вызове той или иной функции из обработчика сигнала в то время, когда главный поток выполняет одну из них, возникает большая путаница, так как эти функции оперируют статическими структурами данных. Как же мы можем вызывать эти две функции в листинге 26.3? Дело в том, что в POSIX требуется, чтобы эти две функции, так же как и многие другие, были безопасными в многопоточной среде (thread-safe). Обычно это достигается с помощью некоторой разновидности синхронизации, осуществляемой внутри библиотечных функций и являющейся для нас прозрачной (то есть незаметной).