【问题标题】:Thread ending, ends the entire program?线程结束,结束整个程序?
【发布时间】:2013-01-23 18:03:59
【问题描述】:

下面的循环在程序的 main 中。它接受传入的连接,并有一个线程使用它。

问题是,一旦任何线程终止,它就会终止整个程序。代码如下:

#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#define BUFLEN 1500
#define MAXCON 30

char *returnTimeDate(int inputchoice);
void readWriteToClient(int inputconnfd);

int main(){

    int backlog = 10;

    int fd;
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        // Error: unable to create socket
    }

    struct sockaddr_in cliaddr;
    socklen_t cliaddrlen = sizeof(cliaddr);

    struct sockaddr_in addr;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(5001);

    if (bind(fd, (struct sockaddr *) &addr, (socklen_t) sizeof(addr)) == -1) {
        fprintf(stderr,"Bind Didn't Work\n");
    }

    if (listen(fd, backlog) == -1) {
        fprintf(stderr,"Listen Didn't Work\n");
    }

    pthread_t *threadsArray = (pthread_t *)calloc(MAXCON, sizeof(pthread_t));
    pthread_t *threadPtr = threadsArray;

    int k;
    for(k = 0; k < MAXCON; k++){
        fprintf(stderr,"Make %d\n",k);
        int connfd;
        connfd = accept(fd, (struct sockaddr *) &cliaddr, &cliaddrlen);
        if (connfd == -1) {
            fprintf(stderr,"Accept Didn't Work\n");
        }
        fprintf(stderr,"Waited\n",k);
        pthread_create( &threadPtr, NULL, readWriteToClient, (void *)connfd);
        threadPtr++;
    }

    pthread_t *threadPtrJoin = threadsArray;

    for(k = 0; k < MAXCON; k++){
        fprintf(stderr,"Join %d\n",k);
        pthread_join( *threadPtrJoin, NULL);
        threadPtrJoin++;
    }

/*  readWriteToClient(connfd);*/

    close(fd);

    return 0;

}

void readWriteToClient(int inputconnfd){

    int connfd = inputconnfd;

    while(1){

        char *dateString = "DATE\r\n";
        char *timeString = "TIME\r\n";
        char *endString = "end";

        char *bufferTime = returnTimeDate(0);
        char *bufferDate = returnTimeDate(1);

        ssize_t i;
        ssize_t rcount;
        char buf[BUFLEN];
        char *toReturn = (char *)malloc(BUFLEN*sizeof(char));
        rcount = read(connfd, buf, BUFLEN);
        if((strcmp (buf, dateString)) == 0){
            strcpy(toReturn, bufferDate);
        }
        if((strcmp (buf, timeString)) == 0){
            strcpy(toReturn, bufferTime);
        }
        if((strcmp (buf, endString)) == 0){
            goto outside;
        }
        if (rcount == -1) {
            // Error has occurred
            printf("Error: rcount -1");
        }

/*      fprintf(stderr,"I have received = %s\n",buf);*/

        if (write(connfd, toReturn, BUFLEN) == -1) {
            fprintf(stderr,"I didn't write = %s\n",buf);    
        }
    }

    outside: return;

}

char *returnTimeDate(int inputchoice){

    time_t timer;
    char *bufferTimee = (char *)malloc(25*sizeof(char));
    char *bufferDatee = (char *)malloc(25*sizeof(char));
    struct tm* tm_info;
    time(&timer);
    tm_info = localtime(&timer);
    strftime(bufferTimee, 25, "%H:%M:%S\n\0", tm_info);
    strftime(bufferDatee, 25, "%d:%m:%Y\n\0", tm_info);

    if(inputchoice == 0){
        return bufferTimee;
    }else{
        return bufferDatee;
    }

}

为什么要这样做?

【问题讨论】:

  • 你确定方法中的代码执行了吗?没见过非void *线程方法
  • 是的,线程就像一个魅力,只是主程序结束。不过出于好奇,您提到的标准约定是什么?
  • 创建线程后你在做什么?
  • @Geesh_SO 方法声明为void * readWriteToClient(void * inputconnfd),调用为pthread_create(&amp;threadPtr, NULL, readWriteToClient, (void *)connfd);
  • 你加入了这些话题吗?

标签: c multithreading networking pthreads


【解决方案1】:

您对 pthread 的使用有一些非常错误的地方。

首先,我认为你的意思是:

int st = pthread_create(&threadPtr[k], NULL, readWriteToClient, (void *)connfd);
if (st != 0) {
    /* handle error */
}

注意以下几点:

  • 我们将pthread_create() 的返回值存储在st 中并处理其错误情况。
  • 我们传递的是&amp;threadPtr[k]pthread_t * 类型),而不是&amp;threadPtrpthread_t ** 类型)。 这可能是您的问题的原因。

但是我对您的代码的主要问题之一是您通过将readWriteToClient 传递给pthread_create() 来调用未定义的行为。 pthread_create 原型如下:

int pthread_create(pthread_t *restrict,
                   const pthread_attr_t *restrict,
                   void *(*start_routine)(void*), 
                   void *restrict arg);

即使您将&amp;threadPtr 更改为&amp;threadPtr[k],您仍会这样称呼它:

int pthread_create(pthread_t *restrict,
                   const pthread_attr_t *restrict,
                   void (*start_routine)(int),      // oops!
                   void *restrict arg);

所以,pthread_create() 接受一个指向一种类型函数的指针,但你传递给它的是一个指向另一种类型函数的指针(C11,6.7.6.3/15,强调我的):

要兼容的两种函数类型,都应指定兼容的返回类型。 此外,参数类型列表,如果两者都存在,则应在数量上达成一致。 参数和省略号终止符的使用; 对应的参数应有 兼容类型。 [...]

指向函数的指针被隐式转换,无论如何都会调用你的函数,但这是非法的,根据标准(C11,6.3.2.3/8,强调我的):

指向一种类型的函数的指针可以转换为指向 另一种类型的功能并再次返回;结果应比较 等于原来的指针。 如果使用转换后的指针调用 类型与引用类型不兼容的函数, 行为未定义

由于您正在调用未定义的行为,因此不知道在您执行此操作后代码的行为方式。

另外,请记住,将 int 转换为 void * 并返回并不能保证不会丢失信息(它是由实现定义的),所以要小心。

如果您在启用警告的情况下编译,您应该在代码中看到一些其他错误,您应该始终这样做。

【讨论】:

    【解决方案2】:

    我敢打赌,你的编译器会发出一些关于指针类型不兼容的警告......

    我的建议是在编译阶段修复所有警告

    如果这样做,您会注意到 pthread 函数的参数必须是指向 void 而不是 int 的指针。

    另外,后一个函数必须返回一个指向 void 的指针,而不仅仅是 void 会导致堆栈大混乱。

    首先,使用连接fds数组

    int connfd[MAXCON];
    

    然后将connfd保存在

    connfd[k] = accept(fd, (struct sockaddr *) &cliaddr, &cliaddrlen);
    

    那么,改变

    pthread_create( threadPtr, NULL, readWriteToClient, connfd);
    

    pthread_create( threadPtr, NULL, readWriteToClient, (void *) &connfd[k]);
    

    改变

    void readWriteToClient(int inputconnfd)
    

    void *readWriteToClient(void *inputconnfd)
    

    然后将其分配给局部变量

    int connfd = *((int *) inputconnfd);
    

    让我知道它是否有效

    【讨论】:

    • 为什么不int connfd = (int) inputconnfd;
    • 因为根据我的固定版本,inputconnfd 现在是指向 void 的指针
    • -1 表示错误和危险的建议。 pthread_create( threadPtr, NULL, readWriteToClient, (void *) &amp;connfd); 无效(如果是,则强制转换为 (void *) 将是不必要且多余的)除非您添加强同步以确保新线程在更改或消失之前读取该值超出范围。在大多数情况下,通过值传递connfd 是可取的,但它需要强制转换为(void *)
    • 您对指向 connfd 的指针是正确的。我应该指定 connfd 必须取自 int 数组。但是也将 int 传递给期望指针的函数是错误的!例如,在我的系统中,int 的大小为 4 字节,而指针的大小为 8 字节!
    • 已修复...现在我看不出有什么问题。如果您同意我的观点,请删除 -1
    猜你喜欢
    • 1970-01-01
    • 2015-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-09
    • 2012-07-13
    相关资源
    最近更新 更多