Russian Qt Forum
Ноябрь 01, 2024, 16:31 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Модель данных для многопоточного использования  (Прочитано 5720 раз)
ammaximus
Гость
« : Декабрь 22, 2011, 15:12 »

Вообщем задача такова - несколько процессов используют модель данных, читают, пишут. Сама модель должна ежесекундно пересчитывать свои поля и удалять устаревшие.

Хотелось бы такую модель данных, которая будет позволять следующее:
1. Доступ к инкапсулированному массиву данных, функции добавления, удаления элементов, обращения к конкретному элементу, а также возможность делать циклический обход элементов.
Это чтобы люди которые моделью будут пользоваться увствовали себя комфортно.
2. Защищенная модель (мьютекс), дающая право записи только одному процессу в определенное время.
3. Модель, содержащая служебные демоны и дающая им полный и быстрый доступ к своим полям.
4. Также неплохо было бы давать возможность демонам выполнятся во время, свободное от обращения других потоков, ну и тем более самих себя (синхронизация демонов).

Сначала я пытался унаследовать этот класс от QThread и обойтись без демонов, но потом дошло, что другие процессы не смогут пользоваться ссылками на объекты этого класса и вообще модель теряет смысл. Потом я написал небольшой демон и включил его в объект этой модели, но все начало летать, видимо так нельзя.
Еще сделанная мною концепция предоставления итератора наружу во 1 будет вылетать при обращении более 1 потока, а во 2 даже если защитить его мьютексом не будет давать возможность потокам использовать его только для чтения((

Вообще я создаю свою первую многопоточную систему и буду рад любым советам)
Код:
#ifndef ABSTRACTMASSIV_H
#define ABSTRACTMASSIV_H

#include <QVector>
#include <QString>
#include <qmutex.h>

class abstractMassiv
{
public:
    abstractMassiv();
    ~abstractMassiv();
    QVector<double*> :: iterator myIterator; // Итератор для сторонних объектов
    QVector<double*> :: iterator begin();
    QVector<double*> :: iterator end();
    int add(int); // Создание по номеру (если существует - вернет 1)
    int add(double*);                                   // Добавление элемента в массив (если существует - вернет 1)
    int del(int); // Удаление по номеру (если неудачно - вернет 1)
    int del(double*);                                   // Удаление по элементу (отождествление, если неудачно - вернет 1)
    double* find(int); // Поиск по номеру
    double* find(double*); // Поиск по элементу (отождествление)

    double* operator[](int); // Перегрузка оператора индексации
protected:
    bool findPos(int, QVector<double*>::iterator &); // Поиск номера элемента массива (по номеру)
    bool findPos(double*, QVector<double*>::iterator &);// Поиск номера элемента массива (по элементу)

    int copy(double*,double*); // Копирование информации из одного элемента в другой

    QVector <double*> myVector;                         // Вектор данных

    QMutex mutex; // Мутекс для массива
};

#endif // ABSTRACTMASSIV_H
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #1 : Декабрь 22, 2011, 18:24 »

Интересно, хотя кто такие демоны? Улыбающийся Принципиальные недостатки очевидны, Вы сами о них пишете: если один из пользователей взял итератор, то что с валидностью этого итератора? Напрашивается др. подход: сделать "просто модель" и на нее накрутить (Q)ReadWriteLock, т.е. в 1 момент времени любое число читающих но 1 (и только 1) пишущий. Сделать все обращения через переходничок который будет гарантировать захват + метод AcquireWrite (без него не обойтись).
Записан
ammaximus
Гость
« Ответ #2 : Декабрь 23, 2011, 12:35 »

Э.. В данном случае под демонами подразумеваются процессы которые будут ежесекундно экстраполировать данные в массиве и погибать одновременно с ним. У каждого экземпляра такой модели должен быть свой служебный процесс.
Идея: внутри модели делать объекты типа massivUtil : public QThread, которые будут останавливаться в деструкторе модели. Допустимо ли это?
Для меня очень важна циклическая обработка извне, желательно чтобы конструкция была попроще, т.к. будет использоваться посторонними. Есть идеи по этому вопросу?
Записан
ammaximus
Гость
« Ответ #3 : Декабрь 23, 2011, 12:45 »

 Обеспокоенный
Меня беспокоит, как реализовать обход большого количества элементов не блокируя надолго мутекс, т.к. на каждой отдельной итерации будет критичным лишь один элемент, а мутексирование итератора тормознет систему надолго, а иначе не будет доступен циклический обход
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Декабрь 23, 2011, 13:22 »

Обеспокоенный
Меня беспокоит, как реализовать обход большого количества элементов не блокируя надолго мутекс, т.к. на каждой отдельной итерации будет критичным лишь один элемент, а мутексирование итератора тормознет систему надолго, а иначе не будет доступен циклический обход
"Просто мутекс" здесь вообще не годится. Нужно ориентироваться на ReadWriteLock имея ввиду: это название условно, более точная формулировка shared/exclusive (access)

Э.. В данном случае под демонами подразумеваются процессы которые будут ежесекундно экстраполировать данные в массиве и погибать одновременно с ним. У каждого экземпляра такой модели должен быть свой служебный процесс.
Идея: внутри модели делать объекты типа massivUtil : public QThread, которые будут останавливаться в деструкторе модели. Допустимо ли это?
Для меня очень важна циклическая обработка извне, желательно чтобы конструкция была попроще, т.к. будет использоваться посторонними. Есть идеи по этому вопросу?
Идеи простые: "не выделываться" (скажем так) ни с какими демонами, а сделать это действие explicit (явным). Если модель сама собой занимается, то известно откуда/по каким причинам, оттуда и надо крутить. Простой пример: нет активных пользователей - и вот кто-то щемится по записи/чтению. Вот тут и время "обновиться" а потом уже разрешить доступ.

Вообще лучше рассуждать так: если хотя бы 1 из пользователей читает модель - никто не может ее менять, необходимо дождаться конца всех чтений. Это ограничение жестко и неприятно, но его не обойти. Если же речь идет о блокировке индивидуальных элементов (не меняя их числа и порядка) - так можно, но это уже др. песня
Записан
ammaximus
Гость
« Ответ #5 : Декабрь 23, 2011, 15:05 »

К сожалению сделать явно не получиться: модель работать будет в реальном времени и за секунду ее прочтут десятки процессов с разным приоритетом - кому то надо чаще кому то нет, так что  таким методом мы можем не дойти до обновления. Хотя это наверное напрасно, скорее всего успеет..
Тогда проблема только одна - обновление должно быть привязано ко времени, а не к подключенным элементам. Можно конечно время засекать и сразу на много экстраполировать, но я хочу чтобы подключенный процесс выполнился как можно быстрее, а не ждал выполнения каких то там служебных задач. К тому же мне не нужна такая точность, чтобы перед каждым запуском была экстраполяция, мне важнее скорость работы основных процессов.
Записан
ammaximus
Гость
« Ответ #6 : Декабрь 23, 2011, 15:13 »

Наклепал демона, вот что получилось. В принципе запустил на основной модели - работает и делает все как нужно.
Код:
class Massivutil: public QThread{
    Q_OBJECT
    Massiv* linkMXT;
public:
    Massivutil(Massiv* );
    void run();
protected slots:
    void work();          // Экстраполяция, обновление

};

Massivutil::Massivutil(Massiv *a){
    linkMassiv=a;
}

void Massivutil::run(){
    QTimer myTimer;
    myTimer.start(linkMassiv->getRenewPeriod());
    connect(&myTimer, SIGNAL(timeout()), this, SLOT(work()));
    exec();
}

void Massivutil::work(){

    linkMassiv->MassivIterator =linkMassiv->begin();
    while(linkMassiv->MassivIterator !=linkMassiv->end())
    {
        if ((*(linkMassiv->MassivIterator))->lifeTime != 0){
            if ((*(linkMassiv->MassivIterator))->currentTime > (*(linkMassiv->MassivIterator))->lifeTime){
                linkMassiv->del(*(linkMassiv->MassivIterator));
                //linkMassiv->MassivIterator++;
            }
            else {
                (*(linkMassiv->MassivIterator))->currentTime+=linkMassiv->getRenewPeriod();
                linkMassiv->MassivIterator++;
            }
        }
        else linkMassiv->MassivIterator++;

    }
}

Попутный вопрос, чтобы тему не создавать, я удаляю что то из вектора erase(iterator). Я зная что erase возвратит указатель на следующий элемент, но что в данный момент стало с самим итератором? В моей функции не возможно получить результат erase, нужно ли переходить к след элементу (итератор++)? У меня в коде и без этого работает (см закомментированный участок)
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Декабрь 23, 2011, 16:15 »

К сожалению сделать явно не получиться:
Может я чего-то не понял, но Вы вообще думаете защищать доступ к данным или как? Вы не можете писать пока кто-то читает, поэтому как раз по таймеру обновление имеет мало шансов прорваться. Нормально накапливать изменения, потом флашить.

Попутный вопрос, чтобы тему не создавать, я удаляю что то из вектора erase(iterator). Я зная что erase возвратит указатель на следующий элемент, но что в данный момент стало с самим итератором?
Это конкретно для каждого контейнера, оговаривается стандартом. Для std::vector итератор становится НЕ валиден.

Наклепал демона, вот что получилось. В принципе запустил на основной модели - работает и делает все как нужно.
Ну так и слава богу - жизнь покажет и жизнь научит  Улыбающийся
Записан
ammaximus
Гость
« Ответ #8 : Декабрь 26, 2011, 08:02 »

Цитировать
Нормально накапливать изменения, потом флашить
Я понимаю, что так лучше, но пока реализация обрисовывается мутно. Спасибо, буду экспериментировать Улыбающийся
Записан
ammaximus
Гость
« Ответ #9 : Декабрь 26, 2011, 12:19 »

Цитировать
по таймеру обновление имеет мало шансов прорваться

Цитировать
To ensure that writers aren't blocked forever by readers, readers attempting to obtain a lock will not succeed if there is a blocked writer waiting for access, even if the lock is currently only accessed by other readers. Also, if the lock is accessed by a writer and another writer comes in, that writer will have priority over any readers that might also be waiting.
Насколько я понял то, что написано в документации, таймерные пишущие потоки смогут попасть внутрь, т.к. имеют приоритет.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Декабрь 28, 2011, 15:32 »

Цитировать
To ensure that writers aren't blocked forever by readers, readers attempting to obtain a lock will not succeed if there is a blocked writer waiting for access, even if the lock is currently only accessed by other readers. Also, if the lock is accessed by a writer and another writer comes in, that writer will have priority over any readers that might also be waiting.
Насколько я понял то, что написано в документации, таймерные пишущие потоки смогут попасть внутрь, т.к. имеют приоритет.
Писатель (writer) не может получить доступ пока все читающие не отстрелялись. Поэтому писатель сначала выставляет флажок (обычно называется типа pending write). Увидев его, новые читатели не захватывают по чтению (нужно пропустить писателя), поэтому писатель ждет только тех кто начали читать до него. Однако все это обеспечивается механизмом ReadWriteLock, а с "просто мутексом" писатель может ждать неограниченно долго.

В обновлении данных по таймеру нет ничего плохого, но так или иначе писатель должен знать что и как обновлять. Напр если ничего не изменилось - вероятно и обновлять нечего. Или если данные обновлены но никто из читателей не успел их прочитать - опять впустую. Др. словами просто так лупить по таймеру - неэффективно
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.054 секунд. Запросов: 23.