Покритикуйте реализацию producer-consumer
От: OdesitVadim Украина  
Дата: 05.12.12 14:41
Оценка:
Hi

Достался мне код на доработку. Основная часть выглядит очень просто

while (need_run) {
  char * data = getData(); // получить данные с устройства
  char * pr_data = processData(data); // обработка
  sendData(pr_data); // по сети отправить потребителю.
}


getData — получает данные с устройства, обычно выполняется за постоянное время, порядка 40 мс. processData — от 5 до 100 мс, но обычно также в районе 40-50мс. ( в зависимости от данных). data — несколько мегабайт, pr_data — пара десяток килобайт. По факту, data — глобальный массив. sendData выполняется быстро.

Так как getData и processData абсолютно независимы, захотелось запустить их в отдельных потоках, тем самым поднять в раза полтора скорость. Родился такой код.


#include "stdafx.h"
#include <system_error>
#include <iostream>
#include <windows.h>
#include <process.h>

const int MSIZE = 1024*1024 * 16;
// два буфера, применяются по очереди
char shareData1[MSIZE];
char shareData2[MSIZE];

uintptr_t thr; // хендл потока для получения данных
HANDLE hEvent1, hEvent2;  // для двух event'ов
volatile bool run = true; // для остановки потока
char * thrData = NULL; // адрес одного из буферов, куда поток будет вставлять свои данные

// процедура потока
void thrProc(void* args) {
    while (run) {
        //код, симулирующий получение данных
        for (int i = 0; i < MSIZE; i++) 
            thrData[i] = i % 256;
        //signal1
        SetEvent(hEvent1);
        // wait2
        WaitForSingleObject( hEvent2, INFINITE );
    }
}

// запуск потока получения данных
void startThread() {
    hEvent1 = CreateEvent( NULL, FALSE, TRUE, NULL );
    hEvent2 = CreateEvent( NULL, FALSE, FALSE, NULL );
        thrData = shareData1;
    thr = _beginthread(thrProc, 0, NULL);
}

// функция получения данных
char * getDataNew()
{
        // если запросили данные первый раз - запускаем поток
    if (thr == NULL) {
        startThread();
    }
    // wait 1
    WaitForSingleObject( hEvent1, INFINITE );
        // меняем буфера местами
    char * data = thrData;
    thrData = (thrData == shareData2)?shareData1:shareData2;
    // signal 2
    SetEvent( hEvent2 );
    return data;
}

// функция обработки данных
int processData( char * data ) 
{
    int s = 0;
        // симулируем обработку данных
    for (int i = 0; i < MSIZE; i++)
        s += data[i];  // то, что здесь переполнение, нас мало интересует
    return s;
}

// остановка потока получения данных
void stopThread() 
{
    run = false;
    SetEvent(hEvent2); // for stop
    WaitForSingleObject( (HANDLE)thr, INFINITE );
}

int _tmain(int argc, _TCHAR* argv[])
{
    for (int i = 0; i < 100; i++) { // 100 для приличия, не делать же бесконечный цикл
        char * data = getDataNew();
        processData(data);
    }
    stopThread();
    return 0;
}

Данный код тестировался примитивным профайлером и показал некоторое ускорение. Правда если уменьшать время получения/обработки данных (параметр MSIZE), то event'ы съедают всю производительность

Покритикуйте пожалуйста. Может можно посмотреть где то на более красивое решение. Цикл в main хочется оставить без изменений (иначе нужно будет много переделывать).
vs2010 producer-consumer
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.