2017-01-31 74 views
2

所以我有非常高的16MB/s的数据采集率。我正在将4MB数据从设备文件读入缓冲区,然后对其进行处理。但是,这种写作和阅读的方法会让项目变慢。我想在C中实现一个双缓冲区。为了简化我对双缓冲区的想法,我决定不包括从设备文件读取以简化读取操作。我创建的是一个C程序,它产生两个独立的线程readThread和writeThread。我让readThread调用我的交换函数,交换缓冲区的指针。在C中实现双缓冲区

这个实现很糟糕,因为我使用互斥量之外的共享内存。我其实有点尴尬地发布它,但它至少会让你知道我正在尝试做什么。但是,我似乎无法提出一种实际的读取和写入方式来同时分离缓冲区,然后在两个线程完成写入和读取操作后调用swap。

有人可以告诉我,如果它可能实现双缓冲,并给我一个如何使用信号来控制线程读取和写入的想法?

请注意,readToBuff(我知道的愚蠢名称)和writeToBuff实际上目前没有做任何事情,它们有空白功能。

这里是我的代码:

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

pthread_t writeThread; 
pthread_t readThread; 
pthread_mutex_t buffer_mutex; 

char buff1[4], buff2[4]; 

struct mutex_shared { 
    int stillReading, stillWriting, run_not_over; 
    char *writeBuff, *readBuff; 
} SHARED; 

void *writeToBuff(void *idk) { 
    while(!SHARED.run_not_over) { 
     SHARED.stillWriting = 1; 
     for(int i = 0; i < 4; i++) { 
     } 
     SHARED.stillWriting = 0; 
     while(SHARED.stillReading){}; 
    }  
    printf("hello from write\n"); 
    return NULL; 
} 

void *readToBuff(void *idk) { 
    while(!SHARED.run_not_over) { 
     SHARED.stillReading = 1; 
     for(int i = 0; i < 4; i++) { 
     } 
     while(SHARED.stillWriting){}; 
     swap(writeThread,readThread); 
    } 

    printf("hello from read"); 
    return NULL; 
} 

void swap(char **a, char **b){ 
    pthread_mutex_lock(&buffer_mutex); 
     printf("in swap\n"); 
     char *temp = *a; 
     *a = *b; 
     *b = temp; 
     SHARED.stillReading = 0; 
     //SHARED.stillWriting = 0; 
    pthread_mutex_unlock(&buffer_mutex); 
} 

int main() { 
    SHARED.writeBuff = buff1; 
    SHARED.readBuff = buff2; 
    printf("buff1 address %p\n", (void*) &buff1); 
    printf("buff2 address %p\n", (void*) &buff2); 

    printf("writeBuff address its pointing to %p\n", SHARED.writeBuff); 
    printf("readBuff address its pointing to %p\n", SHARED.readBuff); 

    swap(&SHARED.writeBuff,&SHARED.readBuff); 

    printf("writeBuff address its pointing to %p\n", SHARED.writeBuff); 
    printf("readBuff address its pointing to %p\n", SHARED.readBuff); 

    pthread_mutex_init(&buffer_mutex,NULL); 

    printf("Creating Write Thread\n"); 

    if (pthread_create(&writeThread, NULL, writeToBuff, NULL)) { 

     printf("failed to create thread\n"); 
     return 1; 
    } 
    printf("Thread created\n"); 
    printf("Creating Read Thread\n"); 
    if(pthread_create(&readThread, NULL, readToBuff, NULL)) { 
      printf("failed to create thread\n"); 
      return 1; 
    } 
    printf("Thread created\n"); 
    pthread_join(writeThread, NULL); 
    pthread_join(readThread, NULL); 
    exit(0); 
} 
+0

你的编译器是否抱怨这行'swap(writeThread,readThread);'? – ccpgh

+2

如果您要提高性能,这种双缓冲解决方案不太可能满足需求。看看[循环缓冲区](https://en.wikipedia.org/wiki/Circular_buffer)(a/k/a环形缓冲区)。 –

+1

我同意@PeretteBarella。这就是他们通常在高性能音频应用和其他高数据速率输入源中使用的。如果您必须等待阅读器或写入器停止运行,那么性能会受到影响,并且锁定和信号也会产生开销。 – clearlight

回答

1

使用一对信号灯的好像它会更容易些。每个线程都有它自己的信号量来指示缓冲区已准备好被读入或写入,并且每个线程都有自己的索引到一个循环的结构数组中,每个结构都包含一个指向缓冲区和缓冲区大小的指针。对于双缓冲,圆形阵列只包含两个结构。

的初始状态设置读出线程的信号计数为2时,读出的索引到所述第一缓冲器中,写入线程信号计数为0,并且写入索引到第一缓冲器。然后创建写线程,立即等待其信号量。

读线程在其信号量上等待非零信号计数(sem_wait),读入缓冲区,设置缓冲区大小,递增写入线程信号计数(sem_post),并将其索引“提前”到循环数组的结构。

写入线程在其信号量上等待非零信号计数(sem_wait),从缓冲区写入(使用read线程设置的大小),递增读取线程信号量计数(sem_post)并“提前”它的索引到圆形阵列的结构。

阅读完毕后,读线程设置的结构的缓冲区大小为零,以指示读取链的末端,然后等待写线程“返回”的所有缓冲区。

结构的圆形阵列可包括比仅2结构的更多,从而允许更多的数据嵌套。

我不得不使用类似的方法进行高速数据捕获,但在这种情况下,输入流比单个硬盘驱动器快,因此使用了两个硬盘驱动器,输出在两个写入线程之间交替。一个写入线程在“偶数”缓冲区上运行,另一个在“奇数”缓冲区上运行。对于Windows而言,它的WaitForMultipleObjects()(几乎每个操作系统都是Posix以外的东西),每个线程可以使用互斥锁和信号量,以及自己的基于链表的消息队列。互斥量控制队列更新的队列所有权,信号量指示队列中的待处理项的数量。为了检索消息,单个原子WaitForMultipleObjects()等待一个互斥量和一个非零信号量计数,并且当两者都发生时,递减信号计数并放开线程。消息发送者只需要互斥体上的WaitForObject()来更新另一个线程消息队列,然后发布(释放)线程信号并释放互斥锁。这消除了线程之间的任何优先级问题。