【发布时间】:2020-04-13 13:39:37
【问题描述】:
我有一个用 C 实现的 HTTP 类 Apache 的 Web 服务器,我的问题是我不知道如何初始化队列(以及如何将线程排入队列),主要是因为我不确定如何在继续当前线程之前检查是否有前一个线程要加入。
服务器可以利用管道请求来提高其响应速度,在一个 更复杂的方式:web 服务器可以为每个请求生成一个新线程 资源,同时准备响应;但是,由于必须归还资源 以与服务器接收请求相同的顺序(FIFO)发送到客户端,它将 在各个响应线程之间进行协调。
这个协调阶段是通过实施一种“医生候诊室”来实现的 其中每位患者在进入时都会询问谁是最后到达的,并对其进行跟踪并 只有在他面前的人离开时才进入医生办公室。这样,每个人都有 队列的部分视图(只关心一个人),但这个部分视图允许正确 FIFO队列的实现。
这里是我必须做什么的描述:
同样,每个新线程都必须存储处理前一个线程的标识符 使用系统调用pthread_join()请求并等待其终止。第一个线程, 显然,不必等待任何人,最后一个线程必须由主线程等待 在关闭连接本身之前处理该连接上的请求的线程和 返回以等待新的连接请求。
我无法正确初始化 to_join 数据结构,主要是因为我不明白如何计算要加入的线程的索引 i。- 我如何区分第一个和最后一个线程在指针数组中?
这是代码(我只能在 TO BE DONE START 和 TO BE DONE END cmets 之间进行修改):
#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;
}
【问题讨论】:
-
您遇到了麻烦,很大程度上是因为您尝试实现的设计有很多问题。它们从高度依赖文件范围的数据结构开始,但绝不止于此。您询问的具体细节似乎源于您没有将线程或任务与他们目前正在执行工作的客户端连接/请求相关联的数据结构。
-
要有效地使用线程池,您需要更好地将要完成的工作与正在执行它的线程分离。我会建议工作线程从中提取的一个或多个任务队列或优先级队列。排序关联应该在需要时在任务之间创建,而不是在线程之间。在每个任务描述结构中实现数据,该结构支持管理响应的线程在每个任务完成之前推迟写入,并且所有其他任务都已经呈现,必须在此之前呈现。
pthread_join()几乎与此无关。 -
请注意,任何依赖
pthread_join()的解决方案都不能描述为使用线程pool,因为pthread_join等待线程终止,而线程池的关键特征是其中的每个线程都可以执行多项工作。一旦线程终止,它就不能再执行任何工作了。 -
感谢您的明确解释@JohnBollinger。我根据您的上一条评论修改了问题,因为我误解了线程池的正确使用。
-
更仔细地查看了代码,这里有些地方很奇怪。在文件范围内有两个
connection_no声明,具有(显然)不同的数组长度,这会产生未定义的行为。此外,其中至少有一个与任务相关,但在函数中是不可访问的,因为它的标识符被局部变量的名称所掩盖。我怀疑我正在查看不打算以这种方式组合的部分的大杂烩,但无论这些问题的根源是什么,这个问题目前是无法回答的。
标签: c multithreading data-structures server pthreads