2017-01-31 11 views
2

Итак, у меня очень высокая скорость сбора данных 16 МБ/с. Я читаю 4 МБ данных в буфер из файла устройства, а затем обрабатываю его. Однако этот метод написания, а затем чтения был медленным для проекта. Я хотел бы реализовать двойной буфер в C.Внедрить двойной буфер в C

Для упрощения моей идеи о двойном буфере я решил не включать чтение из файла устройства для простоты. То, что я создал, является программой на языке C, которая порождает два отдельных потока readThread и writeThread. Я сделал readThread вызовом моей функции свопинга, которая меняет указатели буферов.

Эта реализация ужасна, потому что я использую общую память за пределами Mutex. Я на самом деле немного смущен, чтобы опубликовать его, но он по крайней мере даст вам представление о том, что я пытаюсь сделать. Тем не менее, я не могу придумать практический способ чтения и записи отдельных буферов одновременно, а затем вызов свопа после того, как оба потока закончили писать и читать.

Может кто-нибудь скажет мне, возможно ли реализовать двойную буферизацию и дать мне представление о том, как использовать сигналы для управления, когда потоки читаются и записываются?

Обратите внимание, что 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

Ваш компилятор жалуется на эту «своп» этой строки (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) на его семафоре, пишет из буфера (используя размер, заданный по потоку чтения), увеличивает количество семафоров чтения (sem_post) и «продвигает» его индекс к круговой матрице структур.

Когда чтение завершено, поток чтения устанавливает размер буфера структуры в ноль, чтобы указать конец цепи чтения, а затем ожидает, что поток записи «вернет» все буферы.

Круговой массив структур может включать в себя не более двух структур, что позволяет больше вложенных данных.

Мне пришлось использовать что-то подобное для высокоскоростного захвата данных, но в этом случае входной поток был быстрее, чем один жесткий диск, поэтому использовались два жестких диска, а выход чередовался между двумя потоками записи. Один поток записи работает на «четных» буферах, другой - на «нечетных» буферах.

В случае Windows с WaitForMultipleObjects() (что-то, что имеется в каждой операционной системе, отличной от Posix), каждый поток может использовать мьютекс и семафор, а также собственную очередь сообщений, связанных с связанным списком.Мьютекс контролирует принадлежность очереди для обновлений очередей, семафор указывает количество ожидающих очереди элементов. Для получения сообщения один атомный WaitForMultipleObjects() ожидает мьютекс и счетчик ненулевого семафора, а когда оба они имеют место, уменьшается количество семафоров и разблокирует поток. Отправитель сообщений просто нуждается в WaitForObject() в mutex для обновления очереди очередей нитей, затем отправляет (освобождает) семафор потоков и освобождает мьютекс. Это устраняет любые проблемы с приоритетом между потоками.