Поиск:

Ответ в темуСоздание новой темы Создание опроса
> boost::mutex, помогите разобраться с задачей 
V
    Опции темы
JanryMayers
Дата 24.1.2011, 00:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 71
Регистрация: 11.6.2009
Где: Украина, г. Киев

Репутация: нет
Всего: нет



Здрастье всем!

При решении определённой задачи возник, вопрос необходимо ли использовать mutex, если да то можете объяснить как будет работать программа с ним и без него?

Задача:
 Основной поток запускает x вспомогательных, бесконечно добавляющих в некий контейнер свой элемент, потоков с некоторой изменяющейся задержкой. При этом он удаляет самый старый элемент в случае, если тот был добавлен другим потоком или если количество элементов в контейнере >Y (вот эта часть условия не ясна до конца).

вот кусок кода
Код

class mThread{
public:
    mThread(size_t, size_t, std::list<int>*);
    ~mThread();
    void stop();
private:
    void run();
    boost::shared_ptr<boost::thread> m_thread;
    boost::thread* the_thread;
    std::list<int>* List;
    size_t Y;
    size_t element;
    bool signal;
};
mThread::mThread(size_t x, size_t y, std::list<int>* l) : element(x), Y(y), List(l), signal(true){
    m_thread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&mThread::run, this)));
}

void mThread::run(){
    while(signal){
        if(static_cast<size_t>(List->size()) > Y){
            List->pop_front();
            std::cout<<"\nthread "<<element<<"pop_front";
        }
        else{
            List->push_back(element);
            std::cout<<"\nthread "<<element<<"push_back";
        }
    }
}
mThread::~mThread(){
}
void mThread::stop(){
    signal = false;
    m_thread->join();
}


подскажите что нужно добавить в метод run?

Это сообщение отредактировал(а) JanryMayers - 24.1.2011, 01:38
PM MAIL   Вверх
borisbn
Дата 24.1.2011, 09:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 48
Всего: 135



А где же ?
Цитата(JanryMayers @  24.1.2011,  00:43 Найти цитируемый пост)
При этом он удаляет самый старый элемент в случае, если тот был добавлен другим потоком


Для синхронизации между потоками в твоём случае я бы передавал в потоки не просто указатель на list, а на класс-наследник от list, который бы включал в себя ещё 2 метода - lock и unlock, а также private-член mutex.
Тогда run() выглядел бы след.образом

Код

void mThread::run(){
    while(signal){
        List->lock();
        int lastElem = List.front();
        if(static_cast<size_t>(List->size()) > Y
          || lastElem != element ) // то самое условие, о котором я говорил в начале поста
       {
            List->pop_front();
            std::cout<<"\nthread "<<element<<"pop_front";
        }
        else{
            List->push_back(element);
            std::cout<<"\nthread "<<element<<"push_back";
        }
        List->unlock();
    }
}

Ну, а что должны делать ф-ции lock/unlock - думаю понятно.

И ещё. Правильнее было бы написать конструктор так:
Код

mThread::mThread(size_t x, size_t y, std::list<int>* l)
  : element(x),
     Y(y),
     List(l),
     signal(true),
     m_thread( new boost::thread(boost::bind(&mThread::run, this)) );
{
}


Ну, и раз уж пошла такая пьянка ... smile
Я бы передавал не указатель, а ссылку на list


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
JanryMayers
Дата 24.1.2011, 11:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 71
Регистрация: 11.6.2009
Где: Украина, г. Киев

Репутация: нет
Всего: нет



Спасибо, borisbn. Добавил бы те в репутацию, но постов малавата).

То есть получается mutex должен быть один единственный на все потоки. А какое у него должно быть начальное значение?
И на счет функций  lock/unlock:
Код

cclass Case : public std::list<size_t> {
public:
    Case();
    ~Case();
    void lock();
    void unlock();
private:
    mutable boost::mutex the_mutex;
};
void Case::lock(){
    boost::mutex::scoped_lock lock(the_mutex);
}
void Case::unlock(){
    boost::mutex::scoped_lock unlock(the_mutex);
}

я правильно мысль уловил?

...
Цитата

Я бы передавал не указатель, а ссылку на list


почему? объясни, приведи пример)
у мя щас выглядит так
Код

typedef mThread* mThreadPtr;
int main(int argc , char*argv[] ){
...
    Case the_case;
    mThreadPtr* thrd = new mThreadPtr[X];
    for(size_t i = 0; i<X; i++){
        thrd[i] = new mThread(i+1, Y, &the_case);
    }
...
}


и ещё варнинг warning C4355: 'this' : used in base member initializer list
 на m_thread( new boost::thread(boost::bind(&mThread::run, this)) );


Это сообщение отредактировал(а) JanryMayers - 24.1.2011, 11:58
PM MAIL   Вверх
azesmcar
  Дата 24.1.2011, 13:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


Профиль
Группа: Участник Клуба
Сообщений: 6291
Регистрация: 12.11.2004
Где: Армения

Репутация: 3
Всего: 211



Цитата(JanryMayers @  24.1.2011,  11:57 Найти цитируемый пост)
То есть получается mutex должен быть один единственный на все потоки

да.

Цитата(JanryMayers @  24.1.2011,  11:57 Найти цитируемый пост)
А какое у него должно быть начальное значение?

какое есть. конструктор по умолчанию все сделает.

Цитата(JanryMayers @  24.1.2011,  11:57 Найти цитируемый пост)
я правильно мысль уловил?

нет, классы scoped_lock, unique_lock, shared_lock... и так далее нужны для безопасности исключений. В частности scoped_lock заблокирует мьютекс в конструкторе и разблокирует в деструкторе, т.е.

Цитата(JanryMayers @  24.1.2011,  11:57 Найти цитируемый пост)
void Case::lock(){
    boost::mutex::scoped_lock lock(the_mutex);
}

эффект этой функции - 0!

и еще..надо стараться блокировать как можно меньший участок кода.
В частности, если код выглядет вот так

Цитата(borisbn @  24.1.2011,  09:47 Найти цитируемый пост)
void mThread::run(){
    while(signal){
        List->lock();
        int lastElem = List.front();
        if(static_cast<size_t>(List->size()) > Y
          || lastElem != element ) // то самое условие, о котором я говорил в начале поста
       {
            List->pop_front();
            std::cout<<"\nthread "<<element<<"pop_front";
        }
        else{
            List->push_back(element);
            std::cout<<"\nthread "<<element<<"push_back";
        }
        List->unlock();
    }
}

то смысл использования потоков мне не совсем понятен, все равно все выполняется последовательно.

Добавлено через 1 минуту и 51 секунду
да, и тема тут не совсем в правильном разделе, попрошу ка я модераторов перенести ее в "Общие Вопросы".
PM   Вверх
borisbn
Дата 24.1.2011, 13:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 48
Всего: 135



Цитата(JanryMayers @  24.1.2011,  11:57 Найти цитируемый пост)
Я бы передавал не указатель, а ссылку на listпочему? объясни, приведи пример)

просто мне ссылки больше нравятся. Ты не сможешь передать нулевой указатель (ссылки на 0 не бывает) и тебе, соответственно, не нужно всё время проверять
if ( ptr != 0 ) { ptr-> ...

Цитата(azesmcar @  24.1.2011,  13:16 Найти цитируемый пост)
и еще..надо стараться блокировать как можно меньший участок кода.В частности, если код выглядет вот так

полностью на 100% согласен. просто в коде, представленном ТС, по другому никак. Плюс его программа, скорее всего, либо для обучения потокам и мьютексам, либо она гораздо больше и сложнее, а в том коде lock'ать необходимо, т.к. там front(), size(), pop_front() и push_back() идут непосредственно др.за др.

Цитата(azesmcar @  24.1.2011,  13:16 Найти цитируемый пост)
смысл использования потоков мне не совсем понятен, все равно все выполняется последовательно.

если это - и вся программа, то да, никакого смысла.


Цитата(JanryMayers @  24.1.2011,  11:57 Найти цитируемый пост)
я правильно мысль уловил?

посмотри у boost::mutex должны быть ф-ции типа lock/unlock.
и ещё, в данном конкретном случае модификатор mutable IMHO бессмысленный, тем более, что ф-ции твоего класса Case не константные


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
JanryMayers
Дата 24.1.2011, 16:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 71
Регистрация: 11.6.2009
Где: Украина, г. Киев

Репутация: нет
Всего: нет



Цитата

нет, классы scoped_lock, unique_lock, shared_lock... и так далее нужны для безопасности исключений. В частности scoped_lock заблокирует мьютекс в конструкторе и разблокирует в деструкторе, т.е.

Цитата(JanryMayers @  24.1.2011,  11:57 )
void Case::lock(){
    boost::mutex::scoped_lock lock(the_mutex);
}

эффект этой функции - 0!

теперь правильно?
Код

class Case : public std::list<size_t> {
public:
    Case();
    ~Case();
    void lock();
    void unlock();
private:
    boost::mutex the_mutex;
};
Case::Case(){
}
Case::~Case(){
}
void Case::lock(){
    the_mutex.lock();
}
void Case::unlock(){
    the_mutex.unlock();
}

Цитата

то смысл использования потоков мне не совсем понятен, все равно все выполняется последовательно.

задача у мя такая.
Цитата

и еще..надо стараться блокировать как можно меньший участок кода.

тогда лучше в этом куске кода каждое обращение к Case обработать mutex'ом
Цитата

        int lastElem = List.front();
        if(static_cast<size_t>(List->size()) > Y

если у мя пустой список как я могу от него взять первый элемент?!!

Это сообщение отредактировал(а) JanryMayers - 24.1.2011, 16:30
PM MAIL   Вверх
borisbn
Дата 24.1.2011, 16:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 48
Всего: 135



Цитата(JanryMayers @  24.1.2011,  16:26 Найти цитируемый пост)
теперь правильно?

ага

Цитата(JanryMayers @  24.1.2011,  16:26 Найти цитируемый пост)
тогда лучше в этом куске кода каждое обращение к Case обработать mutex'ом

у тебя и так идёт обращение в каждой строке:

Код

int lastElem = List->front();                   // 1
if(static_cast<size_t>(List->size()) > Y // 2
          || lastElem != element ) 
       {
            List->pop_front();                    // 3
        }
        else{
            List->push_back(element);        // 4



--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
JanryMayers
Дата 24.1.2011, 17:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 71
Регистрация: 11.6.2009
Где: Украина, г. Киев

Репутация: нет
Всего: нет



все таки странное условие, если я его правильно понял то
Код

void mThread::run(){
    while(signal){
        the_case_in_thread->lock();
        if(static_cast<size_t>(the_case_in_thread->size()) >= Y ||((!the_case_in_thread->empty())&&(the_case_in_thread->front()!= element))){
            the_case_in_thread->pop_front();
        }
        else{
            the_case_in_thread->push_back(element);
        }
        the_case_in_thread->unlock();
    }
}

контейнер будет всегда состоять из одного элемента? О_о
а то и вообще пустой


Это сообщение отредактировал(а) JanryMayers - 24.1.2011, 17:51
PM MAIL   Вверх
borisbn
Дата 24.1.2011, 18:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 48
Всего: 135



Цитата(JanryMayers @  24.1.2011,  17:49 Найти цитируемый пост)
все таки странное условие

задание то тебе ставили - тебе и разбираться smile
Цитата(JanryMayers @  24.1.2011,  17:49 Найти цитируемый пост)
контейнер будет всегда состоять из одного элемента?


Цитата(JanryMayers @  24.1.2011,  00:43 Найти цитируемый пост)
бесконечно добавляющих в некий контейнер свой элемент, потоков с некоторой изменяющейся задержкой

если у первого потока задержка 10 сек, а второго одна, и они кладут элементы "1" и "2" соответственно, то в контейнере будет много 2-ек и мало единиц ( или вообще единиц не будет)

и ещё. почему у тебя добавление элемента по else от удаления? Насколько я понял должно быть
ЕСЛИ НУЖНО УДАЛИТЬ
{
   УДАЛИТЬ;
}
ДОБАВИТЬ;


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
azesmcar
Дата 24.1.2011, 21:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


Профиль
Группа: Участник Клуба
Сообщений: 6291
Регистрация: 12.11.2004
Где: Армения

Репутация: 3
Всего: 211



Цитата(JanryMayers @  24.1.2011,  16:26 Найти цитируемый пост)
тогда лучше в этом куске кода каждое обращение к Case обработать mutex'ом

Мьютексом ничего не обрабатывается. Вкратце опишу работу мьютексов.

Мьютекс - объект, имеющий минимум 2 функции - lock() и unlock(). Мьютекс может быть заблокирован одним потоком только один раз (есть и рекурсивные мьютексы, но это другой вопрос). Если мьютекс заблокирован одним потоком, то другой поток при попытке блокировке будет ждать, пока первый его не разблокирует. Таким образом мьютексы обеспечивают последовательность выполнения в параллельном мире. Т.е. часть своей функции ты можешь выполнить НЕ параллельно, а последовательно. Это значит, что эту часть кода (ту, что находиться между lock() и unlock() может выполнить одновременно лишь один поток. Следовательно, если вся функция находиться в критической секции, то и смысла использовать потоки нет, с тем же успехом эта функция может быть вызвана в цикле (вру, не с тем же, циклом будет оптимальнее).

Надеюсь понятно описал.

Цитата(JanryMayers @  24.1.2011,  16:26 Найти цитируемый пост)
теперь правильно?

В принципе да, но
1. от std::list наследовать не стоит, он для этого не предназначен.
2. зачем нужны функции lock() и unlock()? класс (раз уж он потокобезопасен) сам должен понимать когда нужно блокировать мьютекс а когда разблокировать, от пользователя это должно быть скрыто.

Это сообщение отредактировал(а) azesmcar - 24.1.2011, 22:37
PM   Вверх
JanryMayers
Дата 25.1.2011, 01:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 71
Регистрация: 11.6.2009
Где: Украина, г. Киев

Репутация: нет
Всего: нет



спасибо за описание
Цитата

1. от std::list наследовать не стоит, он для этого не предназначен.

а что посоветуете вместо std::list??
или его просто использовать в объект класса Case

Код

void mThread::run(){
        the_case_in_thread->lock();
        while(signal){
            if(static_cast<size_t>(the_case_in_thread->size()) >= Y ||((!the_case_in_thread->empty())&&(the_case_in_thread->front()!= element))){
            the_case_in_thread->pop_front();
        }
    the_case_in_thread->push_back(element);
        the_case_in_thread->unlock();
    }
}

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


Это сообщение отредактировал(а) JanryMayers - 25.1.2011, 03:28
PM MAIL   Вверх
azesmcar
Дата 25.1.2011, 06:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


Профиль
Группа: Участник Клуба
Сообщений: 6291
Регистрация: 12.11.2004
Где: Армения

Репутация: 3
Всего: 211



Цитата(JanryMayers @  25.1.2011,  01:54 Найти цитируемый пост)
а что посоветуете вместо std::list??

Вместо list - ничего, list хорош, но вместо наследования могу посоветовать композицию.
Вообще если браться за задачу правильно, то чтобы сделать лист потокобезопасным его нужно написать самому (можно и на основе стандартного, но попотеть придется).
Тогда это позволит блокировать минимум кода, приведу пример.
Список (std::list) - это структура, которая имеет ссылки на предыдущий и следующий элемент
previous_node <-- node --> next_node
каждая структура к тому же хранит значение.

Сейчас, чтобы добавить элемент в список ты блокируешь весь список, выбора у тебя нет, хотя это тебе и не нужно.
На самом деле, если ты добавляешь элемент в конец списка, другие потоки могут спокойно читать, удалять и добавлять в начало, но в случае полного блокирования этого не будет. Можно решить этот вопрос, если к примеру создавать мьютекс для каждого элемента (накладно, но и тут есть решение) и при работе с конкретным элементом блокировать только его, и его соседей. Это просто пример.
Цитата(JanryMayers @  25.1.2011,  01:54 Найти цитируемый пост)
        the_case_in_thread->lock();
        while(signal){
            if(static_cast<size_t>(the_case_in_thread->size()) >= Y ||((!the_case_in_thread->empty())&&(the_case_in_thread->front()!= element))){
            the_case_in_thread->pop_front();
        }
    the_case_in_thread->push_back(element);
        the_case_in_thread->unlock();

если между lock и unlock выскочит исключение будет deadlock. Используй boost::scoped_lock.

Это сообщение отредактировал(а) azesmcar - 25.1.2011, 08:03
PM   Вверх
borisbn
Дата 25.1.2011, 10:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 48
Всего: 135



Цитата(JanryMayers @  25.1.2011,  01:54 Найти цитируемый пост)

void mThread::run(){
        the_case_in_thread->lock();
        while(signal) {
...
            the_case_in_thread->unlock();
          }
}
вот такой код вроде работает как нужно.


в данном случае у тебя вообще неправильно должно работать, т.к. локируешь ты ДО цикла, а разлокируешь ВНУТРИ. Т.е. если придерживаться этого варианта, а не того, что советует azesmcar (кстати - правильного, но более тяжеловесного), то должно быть как-то так

Код

void mThread::run() {
    while(signal) {
        the_case_in_thread->lock();
        if ( static_cast<size_t>(the_case_in_thread->size()) >= Y ||
             ( (!the_case_in_thread->empty()) && (the_case_in_thread->front()!= element) ) ) {
            the_case_in_thread->pop_front();
        }
        the_case_in_thread->push_back(element);
        the_case_in_thread->unlock();
// а вот здесь нужно поставить задержку, чтобы
// 1. выполнить условие задачи - "... потоков с некоторой изменяющейся задержкой"
// 2. не было бы этого: "только при вводе с клавиатуры при работе потоков, нужно немного ждать, что бы они завершились"
// как в boost сделать sleep не знаю. погугли
    }
}



Это сообщение отредактировал(а) borisbn - 25.1.2011, 10:04


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
JanryMayers
Дата 25.1.2011, 10:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 71
Регистрация: 11.6.2009
Где: Украина, г. Киев

Репутация: нет
Всего: нет



Цитата

с некоторой изменяющейся задержкой

её что нужно рендомить?... если да, то в каком диапозоне, лучше всего?


Цитата

mThread::mThread(size_t x, size_t y, std::list<int>* l)
  : element(x),
     Y(y),
     List(l),
     signal(true),
     m_thread( new boost::thread(boost::bind(&mThread::run, this)) );
{
}

и что делать с этим this? он у мя варнинг сыпет 
warning C4355: 'this' : used in base member initializer list

Это сообщение отредактировал(а) JanryMayers - 25.1.2011, 10:21
PM MAIL   Вверх
borisbn
Дата 25.1.2011, 14:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 48
Всего: 135



Цитата(JanryMayers @  25.1.2011,  10:18 Найти цитируемый пост)
её что нужно рендомить?

повторю
Цитата(borisbn @  24.1.2011,  18:16 Найти цитируемый пост)
задание то тебе ставили - тебе и разбираться 

что означает фраза 
Цитата(JanryMayers @  25.1.2011,  10:18 Найти цитируемый пост)
с некоторой изменяющейся задержкой

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


по поводу warning'a - вообще-то от них необходимо избавляться, но в данном конкретном случае его (C4355) можно отключить, т.к. в стандарте сказано:
Цитата

Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-
initializer is specified. [ Example:
class X {
int a;
int b;
int i;
int j;
public :
const int & r;
X( int i): r(a), b(i), i(i), j(this ->i ) {}
};
initializes X::r to refer to X::a, initializes X::b with the value of the constructor parameter i, initializes X::i with the
value of the constructor parameter i, and initializes X::j with the value of X::i; this takes place each time an object of
class X is created. —end example ] [Note: because the mem-initializer are evaluated in the scope of the constructor, the
this pointer can be used in the expression-list of a mem-initializer to refer to the object being initialized. —end note ]


а boost::bind 100% не будет "refer to the object" being они initialized или не being


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С/С++: Кроссплатформенное программирование, QT/Gtk+/wxWidgets"
JackYF
Любитель
  • В заголовке темы в квадратных скобках обозначьте используемую вами библиотеку, например: [QT],[GTK],[wx].
  • Если вопрос актуален только для некоторой версии библиотеки, либо, если вы пользуетесь не самой последней версией, укажите это. Например: [QT4], [GTK2].
  • Все начинающие изучать Qt - не забудьте зайти сюда.
  • Проставьте несколько ключевых слов темы, чтобы её можно было легче найти.
  • В вопросе укажите полную версию версию библиотеки, а также все дополнительные используемые программные пакеты.
  • Не забывайте пользоваться кнопкой "Код".
  • Телепатов на форуме нет! Задавайте чёткий, конкретный и полный вопрос. Указывайте полностью ошибки компилятора и компоновщика.
  • Новое сообщение должно иметь прямое отношение к тематике этого раздела. Флуд, флейм, оффтопик запрещены.
  • Категорически запрещается обсуждение вареза, "кряков", взлома программ и т.д.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, Любитель.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets | Следующая тема »


 




[ Время генерации скрипта: 0.0620 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.