Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Программирование под Unix/Linux > Параллельное выполнение потоков


Автор: mephis 7.5.2012, 01:34
Здравствуйте. нужно написать программу, которая лезет в БД, проверяет значения в ячейках и запускает таймер (время = значение ячейки) в отдельном потоке. потоки должны выполняться параллельно.
Код

/* 
 * File:   Deamon.h
 * Author: mephis
 *
 * Created on 27 Апрель 2012 г., 10:18
 */

#include <mysql/mysql.h>
#include <vector>
#include "Timer.h"
#include <stdlib.h>
#include <pthread.h>

#ifndef DEAMON_H
#define    DEAMON_H

class Deamon {
public:
    Deamon();
    Deamon(const Deamon& orig);
    virtual ~Deamon();
    bool connectDB(char* host, char* name, char* password, char* database);
    void checkDB();
    void setTimers();
    static void* threadFunction(void* arg);    
private:
    MYSQL *connection;
    MYSQL_RES *result;
    MYSQL_ROW row;
    long time;
    int id;
    int alarm;
    int number;
    std::vector<Timer> curTimer;
};

#endif    /* DEAMON_H */

Код

#include "Deamon.h"

using namespace std;

Deamon::Deamon() {
}

Deamon::Deamon(const Deamon& orig) {
}

Deamon::~Deamon() {
}

bool Deamon::connectDB(char* host, char* name, char* password, char* database){
    this->connection = mysql_init(NULL);
    if(!mysql_real_connect(this->connection, host, name, password, database, 3306, NULL, 0)){
        cout << "MySQL connection fail!" << endl;
    }
    else{
        cout << "MySQL connection success!" << endl;
    }
    
    mysql_query(this->connection, "SELECT * FROM `test timers`");
    this->result = mysql_use_result(this->connection);
    this->number = 0;
    pthread_t myThread[5];
    while ((row = mysql_fetch_row(this->result)) != NULL){
        Timer* tempTimer = new Timer();
        tempTimer->setId(atoi(row[0]));
        tempTimer->setTime(atol(row[1]));
        this->curTimer.push_back(*tempTimer);
        pthread_create(&myThread[this->number-1], NULL, threadFunction, this);
        pthread_join(myThread[this->number-1], NULL);
        this->number++;
    }
    cout << this->curTimer.size();
    return true;
}



void* Deamon::threadFunction(void* arg){
    Deamon* threadDmn = ((Deamon*)(arg));
        threadDmn->curTimer.at(threadDmn->number).startTimer();
}


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

Автор: boostcoder 7.5.2012, 10:26
убери pthread_join() и используй барьер.

Добавлено через 45 секунд
после while()

Автор: xvr 7.5.2012, 11:41
Передавать в pthread_create this в качестве параметра для создаваемого thread'а - плохая идейя. У вас все thread'ы передерутся за этот this, т.к. нет никакой гарантии, что Deamon::threadFunction прочтет данные оттуда раньше, чем цикл while в Deamon::connectDB их перезапишет для следующего потока.
Во вторых - надеюсь, что вызов threadDmn->curTimer.at(threadDmn->number).startTimer(); блокируется, иначе у вас все запущенные потоки немедленно завершаться, до наступления заданного timeout'а (а если так и задумывалось, то зачем вообще нужны потоки?)

Автор: mephis 7.5.2012, 20:51
Цитата(boostcoder @ 7.5.2012,  10:26)
убери pthread_join() и используй барьер.

Добавлено @ 10:26
после while()

можно подробнее объяснить, как это нужно делать?

Автор: boostcoder 7.5.2012, 21:05
гуглить:
pthread_barrier_init()
pthread_barrier_wait()
pthread_barrier_destroy()

Автор: mephis 8.5.2012, 09:36
Код

#include "Deamon.h"

using namespace std;

Deamon::Deamon() {
}

Deamon::Deamon(const Deamon& orig) {
}

Deamon::~Deamon() {
}

bool Deamon::connectDB(char* host, char* name, char* password, char* database){
    this->connection = mysql_init(NULL);
    if(!mysql_real_connect(this->connection, host, name, password, database, 3306, NULL, 0)){
        cout << "MySQL connection fail!" << endl;
    }
    else{
        cout << "MySQL connection success!" << endl;
    }
    
    mysql_query(this->connection, "SELECT * FROM `test timers`");
    this->result = mysql_use_result(this->connection);
    this->number = 0;
    pthread_t myThread[5];
    pthread_barrier_init(&this->bar, NULL, 5);
    while ((row = mysql_fetch_row(this->result)) != NULL){
        Timer* tempTimer = new Timer();
        tempTimer->setId(atoi(row[0]));
        tempTimer->setTime(atol(row[1]));
        this->curTimer.push_back(*tempTimer);
        pthread_create(&myThread[this->number], NULL, threadFunction, this);
        this->number++;
    }
    pthread_barrier_destroy(&this->bar);
    sleep(10);
}



void* Deamon::threadFunction(void* arg){
    Deamon* threadDmn = ((Deamon*)(arg));
        threadDmn->curTimer.at(threadDmn->number-1).startTimer();
                pthread_barrier_wait(&threadDmn->bar);
}


переделал программу, используя барьеры. в дебаг режиме всё работает как часы, а вот в обычном - проблема. как уже заметил товарищ xvr, у меня number инкрементируется раньше, чем заканчивает выполнение pthread_create, поэтому в последней итерации я выхожу за пределы массива myThread и ловлю сегфолт. посоветуйте, пожалуйста, как эту проблему решить.

Автор: boostcoder 8.5.2012, 13:22
для начала, pthread_barrier_wait() перемести в Deamon::connectDB() после цикла.

Автор: mephis 8.5.2012, 13:35
Цитата(boostcoder @ 8.5.2012,  13:22)
для начала, pthread_barrier_wait() перемести в Deamon::connectDB() после цикла.

когда я перемещаю его под while(), таймеры вообще не запускаются.

извиняюсь за криворукость, не туда вставлял. работает.

Автор: boostcoder 8.5.2012, 13:47
Цитата(mephis @  8.5.2012,  13:35 Найти цитируемый пост)
работает.

ошибка с number тоже пропала?

Автор: mephis 8.5.2012, 13:52
Цитата(boostcoder @ 8.5.2012,  13:47)
Цитата(mephis @  8.5.2012,  13:35 Найти цитируемый пост)
работает.

ошибка с number тоже пропала?

нет.
как я понимаю, пока выполняется pthread_create, происходит инкремент number и в функцию попадает уже увеличенное на 1 значение . я обошел это таким способом:
Код

void* Deamon::threadFunction(void* arg){
    Deamon* threadDmn = ((Deamon*)(arg));
    threadDmn->number--;
    threadDmn->curTimer.at(threadDmn->number).startTimer();
}

но я опасаюсь, что на менее/более быстрых процессорах это всё полетит к чертям и будет путаница. поэтому хочу найти нормальное решение проблемы.

Автор: boostcoder 8.5.2012, 14:45
что-то не въезжаю...
твой код расщитан на пять потоков. т.е. ты уверен что в БД всегда пять записей?

Автор: mephis 8.5.2012, 16:15
Цитата(boostcoder @ 8.5.2012,  14:45)
что-то не въезжаю...
твой код расщитан на пять потоков. т.е. ты уверен что в БД всегда пять записей?

нет, там могут быть от 1 до 5 записей.

Автор: boostcoder 8.5.2012, 16:55
т.е. не больше пяти? никогда?

Добавлено через 4 минуты и 21 секунду
а number тебе в какие моменты инкрементировать нужно?

Автор: mephis 8.5.2012, 17:01
Цитата(boostcoder @ 8.5.2012,  16:55)
т.е. не больше пяти? никогда?

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

Добавлено через 50 секунд
Цитата(boostcoder @ 8.5.2012,  16:55)
т.е. не больше пяти? никогда?

Добавлено @ 17:00
а number тебе в какие моменты инкрементировать нужно?

number инкрементировать нужно после того, как отработал pthread_create.

Автор: boostcoder 8.5.2012, 17:15
Цитата(mephis @  8.5.2012,  17:01 Найти цитируемый пост)
после того, как отработал pthread_create. 

сейчас оно так и есть.

не понимаю, в чем проблема?

Автор: mephis 8.5.2012, 17:24
Цитата(boostcoder @ 8.5.2012,  17:15)
Цитата(mephis @  8.5.2012,  17:01 Найти цитируемый пост)
после того, как отработал pthread_create. 

сейчас оно так и есть.

не понимаю, в чем проблема?

да, оно так и есть. проблема в том, что, когда использую number в threadFunction, он уже приходит не со своим значением, а number+1. то есть получается у меня так, что number каким-то образом инкрементируется раньше, чем нужно и в threadFunction он уже попадает инкрементированным. поэтому на первой итерации у меня стартует не 0-й таймер, а 1-й. на последней итерации получаю сегфолт, т.к. number на 1 выше, чем моё количество таймеров.

Автор: boostcoder 8.5.2012, 17:32
тогда тебе нужно не -
Цитата(mephis @  8.5.2012,  17:01 Найти цитируемый пост)
после того, как отработал pthread_create.

а - тогда когда запустилась функция потока.
и почему number не инкрементировать в функции потока?

Автор: mephis 8.5.2012, 17:37
Цитата(boostcoder @ 8.5.2012,  17:32)
тогда тебе нужно не -
Цитата(mephis @  8.5.2012,  17:01 Найти цитируемый пост)
после того, как отработал pthread_create.

а - тогда когда запустилась функция потока.
и почему number не инкрементировать в функции потока?

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

Автор: boostcoder 8.5.2012, 17:39
в функцию потока передай указатель на number. в ней, разыменовываешь его, и инкрементируешь.

Автор: sergioK1 8.5.2012, 22:43
вот тут 

Код

 pthread_create(&myThread[this->number], NULL, threadFunction, this);
        this->number++;


может быть что счетик увеличиваеться до вызова threadFunction,
может быть и нет, но  это решает OS  програмист на это повлиять не может, 


boostcoder
барьер тут не поможет,  

с передачай указателя  да ,  cчетчик должен быть внутри threadFunction - это просто логичнее, 
  поговорка  есть на такой случай ,  
"При правильном дизайне программа пишеться сама "

Автор: xvr 10.5.2012, 14:13
Вынесите number из Deamon вообще, и передавайте его отдельной структурой (вместе с this) на куче:
Код

struct Data {
 int number;
 Deamon* self;
};

bool Deamon::connectDB(char* host, char* name, char* password, char* database){
...
 Data loc_data={0,this};

    while ((row = mysql_fetch_row(this->result)) != NULL){
...
        pthread_create(&myThread[this->number], NULL, threadFunction, new Data(loc_data));
        loc_data.number++;
    }
...
}
void* Deamon::threadFunction(void* arg){
    Data threadDmn = *((Data*)(arg));
    delete ((Data*)(arg));
    threadDmn.self->curTimer.at(threadDmn.number).startTimer();
}

Тогда барьеры не нужны

Автор: mephis 11.5.2012, 11:04
Цитата(xvr @ 10.5.2012,  14:13)
Вынесите number из Deamon вообще, и передавайте его отдельной структурой (вместе с this) на куче:
Код

struct Data {
 int number;
 Deamon* self;
};

bool Deamon::connectDB(char* host, char* name, char* password, char* database){
...
 Data loc_data={0,this};

    while ((row = mysql_fetch_row(this->result)) != NULL){
...
        pthread_create(&myThread[this->number], NULL, threadFunction, new Data(loc_data));
        loc_data.number++;
    }
...
}
void* Deamon::threadFunction(void* arg){
    Data threadDmn = *((Data*)(arg));
    delete ((Data*)(arg));
    threadDmn.self->curTimer.at(threadDmn.number).startTimer();
}

Тогда барьеры не нужны

так и сделал. всё работает.

всем спасибо за помощь.

Автор: xvr 11.5.2012, 11:15
Цитата(mephis @  11.5.2012,  11:04 Найти цитируемый пост)
так и сделал. всё работает.

Кстати, если у вас это не учебная задача, и критично быстродействие при запуске thread'ов, то у этого подхода есть маленький недостаток. На каждое создание thread'а будет вызванна пара new/delete. Этого можно избежать заранее выделив массив из структур Data и передавая в создаваемый thread указатели на элементы этого массива по очереди (только надо внимательно следить за индексом элемента, что бы он не вышел за границы массива и не наложился на все еще используемые элементы)

Автор: mephis 13.5.2012, 13:26
Цитата(xvr @ 11.5.2012,  11:15)
Цитата(mephis @  11.5.2012,  11:04 Найти цитируемый пост)
так и сделал. всё работает.

Кстати, если у вас это не учебная задача, и критично быстродействие при запуске thread'ов, то у этого подхода есть маленький недостаток. На каждое создание thread'а будет вызванна пара new/delete. Этого можно избежать заранее выделив массив из структур Data и передавая в создаваемый thread указатели на элементы этого массива по очереди (только надо внимательно следить за индексом элемента, что бы он не вышел за границы массива и не наложился на все еще используемые элементы)

задача не учебная. и если будут присутствовать 1000 и больше thread-ов, то проблемы быстродействия сразу скажутся. программа должна работать в реальном времени с постоянно меняющимися данными в БД, поэтому каждое промедление будет критично и может привести к сбою. благодарю за замечание, я это обязательно учту.

Автор: sergioK1 13.5.2012, 13:45
Цитата(mephis @ 13.5.2012,  12:26)
 если будут присутствовать 1000 и больше thread-ов, то проблемы быстродействия сразу скажутся. программа должна работать в реальном времени с постоянно меняющимися данными в БД, поэтому каждое промедление будет критично и может привести к сбою. благодарю за замечание, я это обязательно учту.

1000 средов  на одной машине ? 
так может подумать о threadPool 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)