【问题标题】:How to signal the worker threads that there is some work to be finished?如何通知工作线程有一些工作要完成?
【发布时间】:2020-05-18 08:24:46
【问题描述】:

我正在构建一个小型套接字服务器,我想在其中创建一个线程池,然后以 boss-worker 线程模式工作。因此,每当主(老板)收到请求时,它就会从池中传递到其中一个工作线程。

在下面的sn-p中,我尝试创建10个线程。

void* process_data(void* arg) {
    printf("invoked by the created thread");
    while(1) {
          // sleep until woken
          // get item from queue
          // do something
    }
}

int total_threads_to_create = 10;
int total_created = 0;
while(total_created < 10) {
   // create 10 threads
   pthread_t thread;
   int created = pthread_create(&thread, NULL, process_data, NULL);
   if(created == 0) total_created++;
}

while(1) {
   // server accepts the request in an infinite loop
   int socket_fd = accept(ss_fd, (struct sockaddr*)&client_sock,&client_sock_len);

   put_new_request_in_queue();
   // signal to one of the thread that work is available

}

正如您在上面看到的,每个新线程都直接调用process_data 方法。现在我想让process_data 中的线程休眠直到被主线程唤醒。

我该怎么做:

  • process_data中的线程休眠直到被主线程唤醒?
  • 我如何向工作线程发出信号表明有请求得到处理?

【问题讨论】:

  • 条件变量?
  • @Shawn 你能举个例子吗?我读过它们,但不确定如何在这种情况下使用它们。

标签: c multithreading sockets pthreads


【解决方案1】:

正常的解决方案是条件变量和队列。这里的一般模式称为生产者/消费者模式。

您使用互斥锁保护队列,然后使用条件变量唤醒工作人员

制片人

workToDo = generateSomeWork()
acquire mutex
queue.push(workToDo)
cv.notify();
release mutex

消费者

loop:
    acquire mutex
    while queue empty
        wait on cv (releasing mutex while waiting)
    workToDo = queue.pop()
    release mutex
    do(workToDo)

就我个人而言,我还喜欢添加一个布尔标志done,当所有工人清理的时候将其设置为True。因此,当您想干净地退出程序时,您获取互斥锁,将 done 设置为 true,然后在 cv 上广播,这会唤醒所有工作。他们看到 done 已设置,并干净地终止。

【讨论】:

  • 在生产者中,获取互斥体需要什么?你能解释一下吗?
  • 生产者也需要释放互斥锁。 @Amanda 互斥体防止生产者和消费者同时访问队列。
  • @Amanda 队列是共享的,所以如果两个线程同时访问它,你会得到一个导致未定义行为的数据竞争(通常在某个随机时间完全损坏和崩溃你的程序) .互斥锁用于确保一次只有一个人可以访问它。条件变量旨在解决互斥锁的一个限制,即您不持有互斥锁时无法知道数据是否发生变化,而持有互斥锁时数据无法更改。简历提供了一种以安全的方式接收通知而不会出现问题的方法。
【解决方案2】:

你有一个生产者-消费者,使用信号量

您已经编写了"Producer-Consumer Problem" 的示例。

管理生产者-消费者队列最合适的控制机制是使用semaphore

你还需要锁定共享队列

但是,由于您有多个生产者,您还需要一个 mutex 以确保没有两个生产者线程同时尝试写入输出队列(如果不这样做,您将损坏队列数据结构)。

Producer-Consumer 的 Wikipedia 文章为您提供了一个大纲解决方案,但没有使用与您正在使用的 pthreads 库完全相同的函数名称。

Pthreads 信号量实现

pthreads 库通过sem_t 类型和sem_wait()(“等待”)和sem_post()(“信号”)调用实现信号量。

【讨论】:

  • 我倾向于不同意信号量击败条件变量(+互斥锁)作为生产者/消费者问题的“最合适”控制机制。 CV 方法具有一些明显的优势。
  • @John Bollinger 信号量的存在是为了解决简单互斥体在管道两端都能够休眠直到需要操作时的缺点。是的,您可以使用受互斥体保护的标志,但这样做意味着忙于等待。
  • 条件变量是一种同步原语,而不是“互斥保护标志”,尽管有时会结合使用这样的标志。没有条件变量的忙等待——这几乎就是一个重点。
  • 很抱歉,我在您的第一个回复中错过了“条件”一词。是的,它会起作用,但它不是这个程序实际执行的模型那么好。对于此示例,生产者线程正在填充队列,信号量是更好的选择,因为信号量的计数器方面允许生产者预先填充队列,而无需等待消费者取出元素。
  • 我同意信号量具有该属性。我不同意这对他们有利的平衡。当队列本身已经包含所有需要的关于在任何给定时间可以进行多少线程的信息时,计数信号量的计数器方面几乎没有增加。 CV 提供了更有意义的优势,例如在某些情况下根本不需要访问 CV,以及更干净的关机。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多