简体   繁体   English

C Linux中的Pthread信令

[英]Pthread signalling in C Linux

I am working with multi-threading in Linux using Pthread . 我正在使用Pthread在Linux中使用多线程。

Thread1 waits for an IRQ from Driver by polling a character device file (my driver has ISR to catch IRQ from HW). Thread1通过轮询字符设备文件来等待来自驱动程序的IRQ(我的驱动程序具有ISR来从硬件捕获IRQ)。

IRQ -----> Thread1 |-----> Thread2 |-----> Thread3 |-----> Thread4

Whenever Thread1 gets an IRQ, I want send a signal to Thread2 , Thread3 and Thread4 to wake them up and then work. 每当Thread1收到IRQ时,我都想向Thread2Thread3Thread4发送信号以唤醒它们,然后工作。

Now, I am trying to use "pthread conditional variable" and "pthread mutex". 现在,我尝试使用“ pthread条件变量”和“ pthread互斥量”。 But it seems that is not good approach. 但是看来这不是一个好方法。

What is efficient way for synchronization in this case? 在这种情况下,同步的有效方法是什么? Please help. 请帮忙。

Thank you very much. 非常感谢你。

As I understand it, your problem is that your child threads (Threads 2 through 4) don't always wake up exactly once for every IRQ that Thread1 receives -- in particular, it might be that an IRQ is received while the child threads are already awake and working on an earlier IRQ, and that causes them not to be awoken for the new IRQ. 据我了解,您的问题是您的子线程(线程2至4)并不总是针对Thread1收到的每个IRQ完全唤醒一次-特别是,可能是在子线程处于接收状态时收到了IRQ已经醒来并正在使用较早的IRQ,这使得它们不会为新的IRQ所唤醒。

If that's correct, then I think a simple solution is to use a counting semaphore for each child-thread, rather than a condition variable. 如果是正确的话,那么我认为一个简单的解决方案是对每个子线程使用计数信号量 ,而不是条件变量。 A semaphore is a simple data structure that maintains an integer counter, and supplies two operations, wait/P and signal/V. 信号量是一个简单的数据结构,它维护一个整数计数器,并提供两个操作,wait / P和signal / V。 wait/P decrements the counter, and if the counter's new value is negative, it blocks until the counter has become non-negative again. wait / P使计数器递减,如果计数器的新值是负数,它将阻塞直到计数器再次变为非负数。 signal/V increments the counter, and in the case where the counter was negative before the increment, awakens a waiting thread (if one was blocked inside wait/P). signal / V递增计数器,如果计数器在递增之前为负数,则唤醒等待线程(如果在wait / P中阻塞了该线程)。

The effect of this is that in the case where your main thread gets multiple IRQs in quick succession, the semaphore will "remember" the multiple signal/V calls (as a positive integer value of the counter), and allow the worker-thread to call wait/P that-many times in the future without blocking. 这样做的效果是,在您的主线程快速连续获得多个IRQ的情况下,信号量将“记住”多个信号/ V调用(作为计数器的正整数值),并允许工作线程执行以下操作:将来可以多次调用wait / P,而不会阻塞。 That way no signals are ever "forgotten". 这样就不会“遗忘”任何信号。

Linux supplies a semaphore API (via sem_init() , etc), but it's designed for inter-process synchronization and is therefore a little bit heavy-weight for synchronizing threads within a single process. Linux提供了一个信号量API(通过sem_init()等),但是它是为进程间同步而设计的,因此对于在单个进程内同步线程来说有点笨重。 Fortunately, it's easy to implement your own semaphore using a pthreads mutex and condition-variable, as shown below. 幸运的是,使用pthreads互斥锁和条件变量可以很容易地实现自己的信号量,如下所示。

Note that in this toy example, the main() thread is playing the part of Thread1, and it will pretend to have received an IRQ every time you press return in the terminal window. 请注意,在这个玩具示例中,main()线程正在扮演Thread1的角色,并且每次您在终端窗口中按return时,它都会假装已收到IRQ。 The child threads are playing the part of Threads2-4, and they will pretend to do one second's worth of "work" every time Thread1 signals them. 子线程扮演着Threads2-4的角色,并且每次Thread1发出信号时,它们都会假装完成一秒钟的“工作”。 In particular note that if you press return multiple times in quick succession, the child threads will always do that many "work units", even though they can only perform one work-unit per second. 特别要注意的是,如果连续多次按回车键,则子线程将始终执行那么多的“工作单元”,即使它们每秒只能执行一个工作单元。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

struct example_semaphore
{
   pthread_cond_t cond;
   pthread_mutex_t mutex;
   int count;  // acccess to this is serialized by locking (mutex)
};

// Initializes the example_semaphore (to be called at startup)
void Init_example_semaphore(struct example_semaphore * s)
{
   s->count = 0;
   pthread_mutex_init(&s->mutex, NULL);
   pthread_cond_init(&s->cond, NULL);
}

// V:  Increments the example_semaphore's count by 1.  If the pre-increment
//     value was negative, wakes a process that was waiting on the
//     example_semaphore
void Signal_example_semaphore(struct example_semaphore * s)
{
   pthread_mutex_lock(&s->mutex);
   if (s->count++ < 0) pthread_cond_signal(&s->cond);
   pthread_mutex_unlock(&s->mutex);
}

// P:  Decrements the example_semaphore's count by 1.  If the new value of the
//     example_semaphore is negative, blocks the caller until another thread calls
//     Signal_example_semaphore()
void Wait_example_semaphore(struct example_semaphore * s)
{
   pthread_mutex_lock(&s->mutex);
   while(--s->count < 0)
   {
      pthread_cond_wait(&s->cond, &s->mutex);
      if (s->count >= 0) break;
   }
   pthread_mutex_unlock(&s->mutex);
}

// This is the function that the worker-threads run
void * WorkerThreadFunc(void * arg)
{
   int workUnit = 0;
   struct example_semaphore * my_semaphore = (struct example_semaphore *) arg;
   while(1)
   {
      Wait_example_semaphore(my_semaphore);  // wait here until it's time to work
      printf("Thread %p: just woke up and is working on work-unit #%i...\n", my_semaphore, workUnit++);
      sleep(1);  // actual work would happen here in a real program
   }
}

static const int NUM_THREADS = 3;

int main(int argc, char ** argv)
{
   struct example_semaphore semaphores[NUM_THREADS];
   pthread_t worker_threads[NUM_THREADS];

   // Setup semaphores and spawn worker threads
   int i = 0;
   for (i=0; i<NUM_THREADS; i++)
   {
      Init_example_semaphore(&semaphores[i]);
      pthread_create(&worker_threads[i], NULL, WorkerThreadFunc, &semaphores[i]);
   }

   // Now we'll pretend to be receiving IRQs.  We'll pretent to
   // get one IRQ each time you press return.
   while(1)
   {
      char buf[128];
      fgets(buf, sizeof(buf), stdin);
      printf("Main thread got IRQ, signalling child threads now!\n");
      for (i=0; i<NUM_THREADS; i++) Signal_example_semaphore(&semaphores[i]);
   }
}

I like jeremy's answer, but it does have some lacking in that the interrupt dispatcher needs to know how many semaphores to increment on each interrupt. 我喜欢jeremy的回答,但是它确实有一些不足,因为中断调度程序需要知道每个中断要增加多少个信号量。 Also each increment is potentially a kernel call, so you have a lot of kernel calls for each interrupt. 同样,每个增量都可能是内核调用,因此每个中断都有很多内核调用。

An alternate is to understand how pthread_cond_broadcast() works. 另一种方法是了解pthread_cond_broadcast()的工作方式。 I have put an example below: 我在下面放一个例子:

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

#ifndef NTHREAD
#define NTHREAD 5
#endif

pthread_mutex_t Lock;
pthread_cond_t  CV;
int GlobalCount;
int Done;

#define X(y)   do { if (y == -1) abort(); } while (0)

void *handler(void *x) {
    unsigned icount;

    X(pthread_mutex_lock(&Lock));
    icount = 0;
    while (!Done) {
        if (icount < GlobalCount) {
            X(pthread_mutex_unlock(&Lock));
            icount++;
            X(pthread_mutex_lock(&Lock));
        } else {
            X(pthread_cond_wait(&CV, &Lock));
        }
    }
    X(pthread_mutex_unlock(&Lock));
    return NULL;
}

int 
main()
{
    X(pthread_mutex_init(&Lock, NULL));
    X(pthread_cond_init(&CV, NULL));
    pthread_t id[NTHREAD];
    int i;
    for (i = 0; i < NTHREAD; i++) {
        X(pthread_create(id+i, NULL, handler, NULL));
    }
    int c;
    while ((c = getchar()) != EOF) {
        X(pthread_mutex_lock(&Lock));
        GlobalCount++;
        X(pthread_mutex_unlock(&Lock));
        X(pthread_cond_broadcast(&CV));
    }

    X(pthread_mutex_lock(&Lock));
    Done = 1;
    X(pthread_cond_broadcast(&CV));
    X(pthread_mutex_unlock(&Lock));
    for (i = 0; i < NTHREAD; i++) {
        X(pthread_join(id[i], NULL));
    }
    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM