一个线程显示对另一个线程(消费者/生产者)感兴趣

时间:2013-04-16 10:47:55

标签: pthreads mutex semaphore condition-variable

我想有必要让线程(消费者)表达对另一个线程(生产者)制造东西的兴趣。但不是所有的时间。

基本上我想做一次性消费者。理想情况下,生产者通过快乐地了解其业务,直到一个(或许多)消费者表示他们想要某些东西,在这种情况下,生产者会将一些数据推送到一个变量并发出信号表明它已经这样做了。消费者将等到变量被填满。

也必须让一次性消费者能够决定等待太久并放弃等待(la pthread_cond_timedwait

我一直在阅读许多关于同步线程的不同方法的文章和问题。目前我倾向于采用条件变量方法。

我想知道这是否是一个很好的方法(作为线程编程的新手,我可能有相当多的bug),或者如果(ab)使用信号量可能会更好这个情况?或完全不同的东西?只是一个指针变量的原子分配(如果可用)?我目前看不出这些如何安全地工作,可能是因为我试图保持安全,这个应用程序应该运行数月,而不会锁定。我可以在生产者中没有互斥体吗?即:只是发出一个条件变量?

我目前的代码如下:

consumer {
   pthread_mutex_lock(m);

   pred = true; /* signal interest */

   while (pred) {
       /* wait a bit and hopefully get an answer before timing out */
       pthread_cond_timedwait(c, m, t);

       /* it is possible that the producer never produces anything, in which
          case the pred will stay true, we must "designal" interest here,
          unfortunately the also means that a spurious wake could make us miss
          a good answer, no? How to combat this? */
       pred = false;
   }

   /* if we got here that means either an answer is available or we timed out */
   //... (do things with answer if not timed out, otherwise assign default answer)

   pthread_mutex_unlock(m);
}

/* this thread is always producing, but it doesn't always have listeners */
producer {
   pthread_mutex_lock(m);

   /* if we have a listener */
   if (pred) {
      buffer = "work!";

      pred = false;

      pthread_cond_signal(c);
   }

   pthread_mutex_unlock(m);
}

注意:我使用的是现代版Linux,必要时可以使用特定于平台的功能 注2:我使用了看似全局变量m,c和t。但这些对每个消费者来说都是不同的。

高级回顾

我希望一个线程能够注册一个事件,等待一段时间然后继续。理想情况下,多个线程应该可以在同一个线程注册时间和所有线程应该获得相同的事件(时间跨度内的所有事件)。

1 个答案:

答案 0 :(得分:1)

你想要的是类似于c ++中的std::futuredoc)。消费者使用特定功能请求生产者执行任务。该函数创建一个名为 future (或 promise )的结构,包含一个互斥锁,一个与该任务相关的条件变量以及一个结果的void指针,并返回它给来电者。它还将该结构,任务ID和参数(如果有的话)放在由生产者处理的工作队列中。

struct future_s {
    pthread_mutex_t m;
    pthread_cond_t c;
    int flag;
    void *result;
};

// basic task outline
struct task_s {
    struct future_s result;
    int taskid;
};

// specific "mytask" task
struct mytask_s {
    struct future_s result;
    int taskid;
    int p1;
    float p2;
};

future_s *do_mytask(int p1, float p2){
     // allocate task data
     struct  mytask_s * t = alloc_task(sizeof(struct mytask_s));
     t->p1 = p1;
     t->p2 = p2;
     t->taskid = MYTASK_ID;
     task_queue_add(t);
    return (struct future_s *)t;
}

然后,生产者将任务拉出队列,处理它,一旦终止,将结果放在将来并触发变量。

消费者可能会等待未来或做其他事情。

对于可取消的期货,在结构中包含一个标志,表示该任务已取消。未来就是:

  • 交付,消费者是所有者,必须解除分配
  • 取消,制作人仍然是所有者并处置它。

因此,生产者必须在触发条件变量之前检查未来是否已取消。

对于“共享”的未来,该旗帜变成了许多订户。如果数字大于零,则必须交付订单。拥有结果的消费者需要在所有消费者之间做出决定(先到先得?结果是否传递给所有消费者?)。

对未来结构的任何访问都必须是互斥的(适用于条件变量)。

关于队列,可以使用链表或数组(对于容量有限的版本)来实现它们。由于可以同时调用创建期货的函数,因此必须使用锁来保护它们,这通常使用互斥锁实现。