3

我正在编写一个 pthreading 网络应用程序(用 C 语言),并认为我应该创建一个线程来处理每个传入连接。但是,我不知道应该使用哪种设计,因为我必须将连接数限制为固定数量(比如 5)。

在阅读 pthread_join 的手册页时,我发现:

There is no pthreads analog of waitpid(-1, &status, 0), that  is,  "join  with  any terminated
thread". If you believe you need this functionality, you probably need to rethink your
application design.

为什么是这样?我怎样才能实现我的目标?谢谢

4

5 回答 5

1

这是个好问题。

我认为手册页作者的标准推理是需要处理进程,waitpid否则它们会使资源悬而未决。pthreads 的情况并非如此。如果您不需要知道特定线程何时终止(或不需要其返回码),您可以将其设为分离线程;它结束了,就是这样。相反,如果你真的需要加入一个线程,你应该知道你需要加入哪个线程。

进一步考虑,父进程与其子进程之间存在直接的一对多关系。 Waitpid将等待其子代,操作系统将跟踪这些子代并将其交付给父代。无论创建多少代进程,这都会发生 - 父进程收获其子进程。

在线程程序中,任何线程都可以创建其他线程。在这种情况下,包罗万象pthread_join意味着什么?如果所有线程都打算与这个包罗万象的线程连接,那么一切都很好。但是对于某些线程确实需要与其子线程连接而其余线程可以由包罗万象连接的程序呢?join操作系统或 pthreads 如何在不构建大量基础架构的情况下 跟踪每种情况下的应用程序?

我想这是可能的,我想每个人都有机会希望waitpid在 pthreads 中有一个通用的类比,但这可能会带来很多开销,因为在实践中,这主要是一种烦恼。当您发现自己处于有多个线程要加入但您不知道哪个将首先结束的情况时,您可以构建一个队列(或使用管道或其他)并让垂死的线程指示它应该加入.

于 2012-08-06T05:32:02.073 回答
0

由于线程不像进程那样组织在层次结构中,等待“任何”线程意味着任何线程都可以访问有关所有线程的全局信息。在某种数据结构(或其他)中组织此类信息将是一定的开销,因为所有线程的创建和终止都必须经过那里。这种开销是自愿避免的,线程意味着轻量级和快速。

还有另外一个方面,就是有连线都连不上去的,也就是一开始就拆了或者拆了的线。将这样的开销强加给他们将更不可接受。

于 2012-08-06T06:51:37.490 回答
0

如果您要限制并发线程的数量,最好的办法是经常预先创建所有线程(工作者)并让它们在无限循环中运行,等待队列中的工作。

然后主线程负责将工作项(在这种情况下为连接)添加到该队列以供工作人员处理。

这样做的优点是简单和速度。简单,因为您不必担心线程启动或停止,除了一次,它们始终在运行并为工作项提供服务。

出于同样的原因速度。它就像一个固定大小的线程池。它还以最有效的方式处理工作项(工作负载会自动平衡,因为线程仅在完成前一项时才会请求新项)。

在伪代码中,这将类似于以下内容。主线只是简单地将工作项添加到队列中。一旦它完成了所有工作,它可以为每个线程发布一个特殊的完成工作项,然后等待它们全部完成。

def queue workqueue = empty

def main:
    # start the threads

    create threadId[5]
    for each threadId (i):
        threadId[i] = startThread (worker)

    # main work loop, finished with (for example) external signal.

    while not finished:
        get workitem from some source
        add workitem to workqueue

    # Place one special FINISH work item for each thread.

    for each threadId (i):
        add FINISH item to workqueue

    # Wait for all threads to exit, then exit main.

    for each threadId (i):
        wait for threadId[i] to exit

    exit

工作线程同样简单。根据需要获取工作项并处理它们的无限循环。

如果工作项是完成工作项,则退出,保证每个线程获得一个且只有一个完成工作项:

def worker:
    # Simple infinite loop to get work items.

    while true:
        get workitem from workqueue

        # Exit if told to.

        if workitem is a FINISH item:
            break

        # Otherwise, process the item and loop around to request next.

        process workitem

    exit
于 2012-08-06T02:07:07.290 回答
0

请允许我首先回应 paxdiablo 的建议,即您允许线程保持持久性,而不是允许它们终止并重新启动它们。此外,我赞同 selbie 的使用非阻塞 I/O 的建议,但我会将它与您的固定线程结合起来(我认为每个 CPU 只有 1 个)。同时使用这两种方法可以让您实现更高的工作量并最大限度地利用机器的 CPU 资源。

但是,要回答您的问题,如果您希望能够以终止顺序加入线程,则需要线程与收割者进行终止顺序通信。这可以相对简单地使用pipe. 当一个线程终止时,它将其写入tid管道,而收割者读取tid并执行pthread_join.

int tid_pipe[2];
pipe(tid_pipe);

void * thread_proc (void *arg) {
    /* ... */
    /* thread exiting */
    pthread_t me = pthread_self();
    write(tid_pipe[1], &me, sizeof(me));
    return 0;
} 

/* thread reaper */
while (read(tid_pipe[0], &tid, sizeof(tid)) == sizeof(tid)) {
    pthread_join(tid, &retval);
    /* ... */
}

但是,作为替代方案,您可以让您的线程分离运行,而不用担心加入。相反,您可以使用条件变量让您的主线程知道何时可以启动另一个线程。

void * thread_proc (void *arg) {
    pthread_detach(pthread_self());
    /* ... */
    /* thread exiting */
    pthread_mutex_lock(&m);
    if (thread_count++ == 0) pthread_cond_signal(&c);
    pthread_mutex_unlock(&m);
    return 0;
}

/* thread spawner */
while (waiting_for_work()) {
    pthread_mutex_lock(&m);
    while (thread_count == 0) pthread_cond_wait(&c, &m);
    pthread_mutex_unlock(&m);
    /* ... handle work with new thread ... */
}
于 2012-08-06T05:53:31.990 回答
-1

如果它是一个每个客户端一个线程的多线程服务器,只需将它们计数出来并重新计数。在客户端线程创建时(即在 accept() 线程中)以及在客户端-服务器线程退出之前使用“clientCount”int 的原子 inc/dec,如果计数 >5,则不再接受()任何连接, (即直接从 accept() 线程发出“连接太多,稍后再试”页面,而不是创建新的服务器-客户端线程)。

试着忘记“加入”——想象一下你从未读过它。

于 2012-08-07T04:40:48.503 回答