Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
pthread C中的线程队列-web服务器repsonse管道_C_Multithreading_Data Structures_Server_Pthreads - Fatal编程技术网

pthread C中的线程队列-web服务器repsonse管道

pthread C中的线程队列-web服务器repsonse管道,c,multithreading,data-structures,server,pthreads,C,Multithreading,Data Structures,Server,Pthreads,我有一个类似HTTP Apache的web服务器,它是用C实现的,我的问题是我不知道如何初始化队列,因此也不知道如何将线程排入队列,主要是因为我不确定在继续当前线程之前,如何检查是否有前一个线程要加入 服务器可以利用管道请求来提高响应速度,在 更复杂的方法是:web服务器可以为每个新线程的请求生成一个新线程 资源,同时准备响应;但是,由于必须返回资源 按照服务器FIFO接收请求的相同顺序发送到客户端,它将 在各个响应线程之间进行协调阶段 这一协调阶段是通过实施一种医生候诊室来实现的 每个患者在进

我有一个类似HTTP Apache的web服务器,它是用C实现的,我的问题是我不知道如何初始化队列,因此也不知道如何将线程排入队列,主要是因为我不确定在继续当前线程之前,如何检查是否有前一个线程要加入

服务器可以利用管道请求来提高响应速度,在 更复杂的方法是:web服务器可以为每个新线程的请求生成一个新线程 资源,同时准备响应;但是,由于必须返回资源 按照服务器FIFO接收请求的相同顺序发送到客户端,它将 在各个响应线程之间进行协调阶段

这一协调阶段是通过实施一种医生候诊室来实现的 每个患者在进入时都会询问谁是最后一个到达的,并对其进行跟踪和记录 只有当前面的人离开时才进入医生的办公室。就这样,每个人都有 队列的局部视图只关注一个人,但此局部视图允许正确的 FIFO队列的实现

以下是我必须做的事情的描述:

同样,每个新线程都必须存储处理前一个线程的线程的标识符 使用系统调用pthread_join请求并等待其终止。第一条线, 显然,不必等待任何人,最后一个线程必须由主线程等待 在关闭连接之前处理该连接上的请求的线程,以及 返回以等待新的连接请求

我无法正确初始化to_join数据结构,主要是因为我不知道如何计算要连接的线程的索引I。-如何区分指针数组中的第一个线程和最后一个线程

以下是我只能在待完成开始和待完成结束注释之间修改的代码:

#include "incApache.h"

pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mime_mutex = PTHREAD_MUTEX_INITIALIZER;

int client_sockets[MAX_CONNECTIONS]; /* for each connection, its socket FD */
int no_response_threads[MAX_CONNECTIONS]; /* for each connection, how many response threads */

pthread_t thread_ids[MAX_THREADS];
int connection_no[MAX_THREADS]; /* connection_no[i] >= 0 means that i-th thread belongs to connection connection_no[i] */
pthread_t *to_join[MAX_THREADS]; /* for each thread, the pointer to the previous (response) thread, if any */

int no_free_threads = MAX_THREADS - 2 * MAX_CONNECTIONS; /* each connection has one thread listening and one reserved for replies */
struct response_params thread_params[MAX_THREADS - MAX_CONNECTIONS]; /* params for the response threads (the first MAX_CONNECTIONS threads are waiting/parsing requests) */

pthread_mutex_t threads_mutex = PTHREAD_MUTEX_INITIALIZER; /* protects the access to thread-related data structures */

pthread_t thread_ids[MAX_CONNECTIONS];
int connection_no[MAX_CONNECTIONS];

void *client_connection_thread(void *vp) {
    int client_fd;
    struct sockaddr_storage client_addr;
    socklen_t addr_size;
    pthread_mutex_lock(&threads_mutex);
    int connection_no = *((int *) vp);

    /*** properly initialize the thread queue to_join ***/
/*** TO BE DONE 3.1 START ***/
        //to_join[0] = thread_ids[new_thread_idx];
    //pthread_t *first;     Am I perhaps supposed to initialize the to_join data structure as a queue with two pointers
    //pthread_t *last;      indicating the first and last element? How can I do it on an array of pointers?
/*** TO BE DONE 3.1 END ***/

    pthread_mutex_unlock(&threads_mutex);
#endif
    for (;;) {
        addr_size = sizeof(client_addr);
        pthread_mutex_lock(&accept_mutex);
        if ((client_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &addr_size)) == -1)
            fail_errno("Cannot accept client connection");
        pthread_mutex_unlock(&accept_mutex);
        client_sockets[connection_no] = client_fd;
        char str[INET_ADDRSTRLEN];
        struct sockaddr_in *ipv4 = (struct sockaddr_in *) &client_addr;
        printf("Accepted connection from %s\n", inet_ntop(AF_INET, &(ipv4->sin_addr), str, INET_ADDRSTRLEN));
        manage_http_requests(client_fd
                , connection_no);
    }
}

#pragma clang diagnostic pop
void send_resp_thread(int out_socket, int response_code, int cookie,
              int is_http1_0, int connection_idx, int new_thread_idx,
              char *filename, struct stat *stat_p)
{
    struct response_params *params =  thread_params + (new_thread_idx - MAX_CONNECTIONS);
    debug(" ... send_resp_thread(): idx=%lu\n", (unsigned long)(params - thread_params));
    params->code = response_code;
    params->cookie = cookie;
    params->is_http1_0 = is_http1_0;
    params->filename = filename ? my_strdup(filename) : NULL;
    params->p_stat = stat_p;
    pthread_mutex_lock(&threads_mutex);
    connection_no[new_thread_idx] = connection_idx;
    debug(" ... send_resp_thread(): parameters set, conn_no=%d\n", connection_idx);

    /*** enqueue the current thread in the "to_join" data structure ***/
/*** TO BE DONE 3.1 START ***/
    //Again, should I use a standard enqueue implementation? But then how would I keep track of the last node ot arrive?
/*** TO BE DONE 3.1 END ***/

    if (pthread_create(thread_ids + new_thread_idx, NULL, response_thread, connection_no + new_thread_idx))
        fail_errno("Could not create response thread");
    pthread_mutex_unlock(&threads_mutex);
    debug(" ... send_resp_thread(): new thread created\n");
}

void *response_thread(void *vp)
{
    size_t thread_no = ((int *) vp) - connection_no;
    int connection_idx = *((int *) vp);
    debug(" ... response_thread() thread_no=%lu, conn_no=%d\n", (unsigned long) thread_no, connection_idx);
    const size_t i = thread_no - MAX_CONNECTIONS;
    send_response(client_sockets[connection_idx],
              thread_params[i].code,
              thread_params[i].cookie,
              thread_params[i].is_http1_0,
              (int)thread_no,
              thread_params[i].filename,
              thread_params[i].p_stat);
    debug(" ... response_thread() freeing filename and stat\n");
    free(thread_params[i].filename);
    free(thread_params[i].p_stat);
    return NULL;
}

我无法正确初始化to_join数据结构, 主要是因为我不知道如何计算 要加入的线程-如何区分中的第一个线程和最后一个线程 指针数组

赋值不同于初始化,对一个元素的操作不同于对整个数组的操作。据我所知,您实际上不需要初始化以加入该函数,因此该注释具有误导性。相反,您只需要为单个元素指定一个适当的值

根据我对各种全局变量的名称、范围和文档注释的解释,以及所讨论函数的名称、签名和初始行,进行分析:

各种数组似乎保存着与多个连接的多个线程相关的数据,因为其中一个文件作用域连接数组的作用是将线程与连接相关联。 该函数似乎是连接相关线程的线程启动函数。 在任何其他连接关联线程正在运行时启动的线程都不应执行与自身相关的设置数据以外的任何操作,以免破坏其他线程和连接所依赖的数据。 现在,至于实际问题-如何确定新线程应该加入哪个线程?你不能。至少,不依赖于问题中提供的模板代码,未经修改*

假设,如果您可以访问将线程与连接关联的connection\u no数组的版本,那么您可以使用它来查找与当前连接关联的所有线程的索引。然后,您可以从相应的thread_id数组中获取它们的线程id,注意这里有另一个名称冲突,以及它们的连接目标从join_到数组。连接的第一个线程是未连接到另一个线程的线程,最后一个线程是未被任何其他线程连接的线程。这种分析并不完全直截了当,但也没有真正的诀窍。细节是留给他们的演习,他们是打算

但是,即使解决了文件作用域名称冲突,也无法执行上述分析,因为文件作用域连接\u no数组在允许插入代码的整个区域内被同名的局部变量遮挡*

还请注意,您似乎需要为新线程选择一个线程索引,该索引通常不会为0。看起来您需要扫描线程ID或连接\u no数组才能找到可用的索引

*除非你作弊。我的意图是让您只将代码插入到client_connection_thread函数的主体中,但实际上,您可以通过将代码插入指定区域将该函数拆分为两个或多个。如果连接的第二个文件作用域声明为\u no和threa d_ID在实践中被假定为被忽略或丢失,那么分解函数可以为阴影问题提供一个解决方案。例如:

    /*** properly initialize the thread queue to_join ***/
/*** TO BE DONE 3.1 START ***/

    return client_connection_thread_helper1(connection_no);
}  // end of function

// parameter 'con' is the number of this thread's connection
void *client_connection_thread_helper1(int con) {
    int my_index;
    // ... Find an available thread index (TODO: what if there isn't one?) ...
    thread_ids[my_index] = pthread_self();
    connection_no[my_index] = con;  // connection_no is not shadowed in this scope

    pthread_t *last = NULL;
    // ... Find the last (other) thread associated with connection 'con', if any ...
    // You can determine the first, too, but that does not appear to be required.

    to_join[my_index] = last;

    return client_connection_thread_helper2(con);
}

// A second additional function is required for the remaining bits of
// client_connection_thread(), because they need the local connection_no
void *client_connection_thread_helper2(int connection_no) {
    int client_fd;
    struct sockaddr_storage client_addr;
    socklen_t addr_size;

/*** TO BE DONE 3.1 END ***/


    pthread_mutex_unlock(&threads_mutex);

我想,找出这种功能拆分的需要和实现可能是练习的一部分,但那将是一个肮脏的把戏,总的来说,这项工作很可能只是形式不好。

您遇到的问题很大程度上是因为您试图实现的设计存在许多问题。它们从严重依赖文件范围的数据结构开始,但决不会到此为止。您询问的具体细节似乎是由于您没有将线程或任务与当前正在执行工作的客户端连接/请求关联的数据结构。要有效使用线程池,您需要更好地将要执行的工作与正在执行的线程解耦。我建议使用一个或多个任务队列或优先级队列,工作线程从中提取。应该在需要的任务之间创建排序关联,而不是在线程之间。在每个任务描述结构中实现数据,该结构支持线程管理在每个任务完成之前暂停写入的响应,以及在完成之前必须呈现的所有其他响应。pthread_join几乎与此无关。请注意,任何依赖pthread_join的解决方案都不能被描述为使用线程池,因为pthread_join等待线程的终止,而线程池的关键特征是其中的每个线程都可以执行多个工作。一旦一个线程终止,它就不能再执行任何工作了。感谢您对@JohnBollinger的清晰解释。我根据你最后的评论修改了这个问题,因为我误解了线程池的正确用法。文件作用域中有两个连接\u no声明,其数组长度明显不同,这会产生未定义的行为。此外,其中至少有一个与任务相关,但在函数中无法访问,因为其标识符被局部变量的名称隐藏。我怀疑我看到的是一堆不打算以这种方式组合的零件,但无论这些问题的根源是什么,这个问题都是无法回答的。