Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Почему deadlock пропадает? а должен оставаться... 
V
    Опции темы
KaraKum
  Дата 20.2.2011, 01:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 640
Регистрация: 3.12.2007

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



Доброе время суток.

В коде:
Код

#include <cstdio>
#include <boost/thread.hpp>
#include <boost/thread/condition.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include <deque>
#include <cstdlib>
#include <time.h>

std::deque<int> buffer;
boost::mutex bufferMutex;


boost::condition bufferHasSome;
boost::condition bufferEmpty;

void Reader()
{
    boost::mutex mutex;
    boost::mutex::scoped_lock lock(mutex);

    //чтение с максимальной скоростью:
    while(true)
    {
WAIT:
        bufferMutex.lock();
        const int size = buffer.size();
        bufferMutex.unlock();
        if(size <= 0) //1.1
        {
            bufferHasSome.wait(lock); //1.2
            goto WAIT;
        }

        bufferMutex.lock();
        while(buffer.size() > 0)
        {
            printf("%d\n", buffer.front());
            buffer.pop_front();
        }
        bufferMutex.unlock();

        //всё прочитано:
        bufferEmpty.notify_one();
    }
}

void Writer()
{
    boost::mutex mutex;
    boost::mutex::scoped_lock lock(mutex);

    int index = 0;

    while(true)
    {
        //добавить порцию (допустим это команда или запрос в базу данных):
        for(int i = rand() % 5; i >= 0; i--)
        {
            bufferMutex.lock();
            buffer.push_back(index);
            bufferMutex.unlock(); //2.1

            bufferHasSome.notify_one(); //2.2

            index++;
            boost::this_thread::sleep(boost::posix_time::milliseconds(rand() % 10));
        }

        //определённо ждём пока порция данных будет считана (хмм, например пока выполнится команда или отправится запрос в базу данных):
WAIT:
        bufferMutex.lock();
        const int size = buffer.size();
        bufferMutex.unlock();
        if(size > 0)
        {
            bufferEmpty.wait(lock);
            goto WAIT;
        }
    }
}

int main()
{
    srand(time(NULL));

    boost::thread readerThread(Reader);
    boost::thread writerThread(Writer);

    getchar();

    return 0;
}

возникает deadlock, думаю, по следующим причинам:
выполнение останавливается после 1.1 (где size = 0) в потоке Reader и переключается на поток Writer где добавляется index в buffer (2.1) и оповещается bufferHasSome (2.2) (но никто этого оповещение ещё не ожидает и, поэтому, это пустая операция); затем процессор переключается назад на поток Reader (на 1.2) и начинает ждать пока кто-нибудь не заполнит buffer, но единственный кто его может заполнить - ожидает пока его кто-нибудь не освободит.
Программа замораживается (возникает deadlock) примерно после 150 итераций - думаю именно по этому причине.
Но если boost::mutex mutex; вынести в глобальное пространство, то deadlock уже больше не возникает (прогонял 200000 итераций) - почему?  smile 

Это сообщение отредактировал(а) KaraKum - 20.2.2011, 01:23
PM MAIL WWW   Вверх
azesmcar
Дата 20.2.2011, 12:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

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



KaraKum

Честно говоря весь код не смотрел, но function-scope mutex привлек мое внимание, как абсолютно бессмысленная вещь. У каждой функции и у каждого потока будет свой mutex, который естественно не будет ничего защищать. Возможно оттого и проблема. Чтобы защищать какие-то общие данные для нескольких потоков, mutex должен быть общим для этих потоков, в этом весь его смысл. Один поток блокирует mutex, остальные ждут, пока тот разблокирует.

Это сообщение отредактировал(а) azesmcar - 20.2.2011, 12:40
PM   Вверх
KaraKum
Дата 20.2.2011, 13:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 640
Регистрация: 3.12.2007

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



Это не "защищающий" мьютекс, или как он был назван. Локальные мьютексы созданы для того чтобы можно было создать boost::mutex::scoped_lock, который, в свою очередь, нужен для того чтобы создать boost::condition. То есть локальные мьютексы не используются для lock/unlock. В том-то и дело что если вынести эти мьютексы (то есть объединить) в глобальное пространство, то deadlock уже не возникает (хотя должен оставаться, ибо структура кода не меняется).
PM MAIL WWW   Вверх
azesmcar
Дата 21.2.2011, 08:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

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



Цитата(KaraKum @  20.2.2011,  13:54 Найти цитируемый пост)
Локальные мьютексы созданы для того чтобы можно было создать boost::mutex::scoped_lock, который, в свою очередь, нужен для того чтобы создать boost::condition. 

А зачем это нужно? тебе нужно ожидать с помощю фиктивного мьютекса? зачем это вообще? у тебя что-то не так с принципами. Ты используешь вещи не так, как они рассчитаны быть использованы. К тому же для reader/writer в boost вообще-то есть boost::shared_mutex.

PM   Вверх
KaraKum
Дата 21.2.2011, 09:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 640
Регистрация: 3.12.2007

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



Принципы тут ни причём. Код надо было смотреть - boost::condition-то ведь используется одна. Я разобрался в этом. deadlock-а не было только потому scoped_lock создавался в самом начале потока и, следовательно, это было (становилось) вовсе не многопоточное приложение при глобальном мьютексе - потоки выполнялись по-очерёдно.
И, кстати, для каждого boost::condition нужно создавать свой собственный мьютекс - как в примере  smile 
PM MAIL WWW   Вверх
azesmcar
Дата 21.2.2011, 10:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

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



Цитата(KaraKum @  21.2.2011,  09:57 Найти цитируемый пост)
Принципы тут ни причём. 

еще как причем.

Цитата(KaraKum @  21.2.2011,  09:57 Найти цитируемый пост)
Код надо было смотреть

читать многопоточный код с кучей goto - увольте smile 

Цитата(KaraKum @  21.2.2011,  09:57 Найти цитируемый пост)
при глобальном мьютексе - потоки выполнялись по-очерёдно.

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

Цитата(KaraKum @  21.2.2011,  09:57 Найти цитируемый пост)
И, кстати, для каждого boost::condition нужно создавать свой собственный мьютекс - как в примере    

да, только не нужно создавать по одному на каждый поток.

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

Это сообщение отредактировал(а) azesmcar - 21.2.2011, 10:17
PM   Вверх
KaraKum
Дата 22.2.2011, 13:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 640
Регистрация: 3.12.2007

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



Цитата(azesmcar @  21.2.2011,  11:01 Найти цитируемый пост)
В этом и есть смысл мьютекса.

Смысл мьютекса не только в этом, а ещё в том чтобы замораживать потоки и обеспечивать выгрузку кэша процессора. То есть ты угадал на 33%.

Цитата(azesmcar @  21.2.2011,  11:01 Найти цитируемый пост)
да, только не нужно создавать по одному на каждый поток.

Для boost::condition именно так и нужно - по одному мьютексу на boost::condition.

Цитата(azesmcar @  21.2.2011,  11:01 Найти цитируемый пост)
Если тебе нужно сделать так, чтобы лишь бы как-то работало, то так и пиши.

Так изначально и работало - вопрос и стоял в форме "почему работает?", хотя должен был возникать deadlock.

Цитата(azesmcar @  21.2.2011,  11:01 Найти цитируемый пост)
То, что ты пытаешься делать с помощью нескольких разных мьютексов, автоматически делает condition_variable с помощью одного, глобального.

Вопрос был про реализацию потоков от буста, а не в решении какой-то задачи. Код портировал из реализации потоков на WinAPI, но ничего подногобно boost::mutex::lock и boost::condition не видел ни в WinAPI, ни в posix threads.
Узналось что деструктор boost::mutex::lock освобождает мьютекс да и boost::condition.notify_one(), при этом, становится блокированным.
Безосновательная полемика, но пока формируется петиция на форум, то как-то лучше понимается своя же задача - теперь реализация буста более-менее понятна  smile 
PM MAIL WWW   Вверх
azesmcar
Дата 22.2.2011, 15:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

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



Цитата(KaraKum @  22.2.2011,  13:43 Найти цитируемый пост)
Смысл мьютекса не только в этом, а ещё в том чтобы замораживать потоки и обеспечивать выгрузку кэша процессора. То есть ты угадал на 33%.

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

Цитата(KaraKum @  22.2.2011,  13:43 Найти цитируемый пост)
Для boost::condition именно так и нужно - по одному мьютексу на boost::condition.

На boost::condition а не на поток, это совершенно разные вещи.
Цитата(KaraKum @  22.2.2011,  13:43 Найти цитируемый пост)
Так изначально и работало - вопрос и стоял в форме "почему работает?", хотя должен был возникать deadlock.

У тебя что, цель сделать программу не рабочей? В конечном итоге ты пишешь программу, которая должна работать, или я чего-то не понимаю?

Цитата(KaraKum @  22.2.2011,  13:43 Найти цитируемый пост)
boost::mutex::lock

ты про lock_guard? Это обычный RAII wrapper. lock (читай EnterCriticalSection) в конструкторе и unlock (читай LeaveCriticalSection) в деструкторе.

Цитата(KaraKum @  22.2.2011,  13:43 Найти цитируемый пост)
boost::condition не видел ни в WinAPI

Да пожалуйста
http://msdn.microsoft.com/en-us/library/ms...2(v=vs.85).aspx

Цитата(KaraKum @  22.2.2011,  13:43 Найти цитируемый пост)
posix threads

Цитата

man pthread_cond_t

Там велосипедов нет. Все есть и на WinAPI и в POSIX.

Добавлено через 10 минут и 54 секунды
вообще алгоритм работы condition-а довольно прост.
ему передается заблокированный мьютекс, когда condition помещается в ожидание, он разблокирует мьютекс и начнет спать до тех пор, пока его не разбудит какой нибудь notify_one или notify_all. Далее, он просыпается и снова блокирует мьютекс. Т.е. когда он не спит - мьютекс заблокирован и можно обращаться к общим данным.

Это сообщение отредактировал(а) azesmcar - 22.2.2011, 15:13
PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




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


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

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