Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: множественное чтение и единичная запись через мьютексы
Форум на CrossPlatform.RU > Библиотеки > boost
Mifodix
Всем доброго времени!
Чтобы проще описать мою проблему, рассмотрим следующую программу:
#include <boost/thread.hpp>
#include <list>
#include <iostream>

boost::shared_mutex shmutex;
std::list<int> l;

void thread_func_read(int num)
{
    //boost::shared_lock<boost::shared_mutex> readLock(shmutex);
        std::cout<<"Thread read "<<num<< " Data: ";
    for (std::list<int>::iterator it=l.begin(); it!=l.end(); ++it)
    {
        std::cout<<*it<<" ";
    }
    std::cout<<std::endl;
    sleep(5);
}

void thread_func_write(int num)
{
    std::cout<<"Thread write "<<num<< " Data: ";
    l.push_front(100);
    std::cout<<std::endl;
    sleep(5);
}

int main()
{
    boost::thread_group thrds;
    l.push_back(10);
    l.push_back(20);
    l.push_back(30);
    l.push_back(40);
    thrds.add_thread(new boost::thread(thread_func_read, 1));
    thrds.add_thread(new boost::thread(thread_func_read, 2));
    thrds.add_thread(new boost::thread(thread_func_read, 3));
    thrds.add_thread(new boost::thread(thread_func_write, 4));
    thrds.add_thread(new boost::thread(thread_func_write, 5));
    thrds.join_all();
    return 0;
}

Необходимо как-то реализовать через мьютексы множественное чтение списка (т. е. все потоки, использующие thread_func_read могут одновременно читать) и единичную запись (т. е. только один поток в настоящее время может производить запись в список). При этом запись в список должна быть возможна только после завершения чтения этого списка другими потоками, а само чтение невозможно, если в список производится запись. Несколько потоков не могут писать одновременно.
С первой часть всё понятно: нужно использовать shared_lock. А вот как быть с ограничением на запись?
Заранее спасибо!
Алексей1153
и чтение, и запись должны одинаково закрываться в "критическую секцию". При чтении нужно быстренько копировать элемент из списка во внешнюю (относительно секции) переменную, удалять, если это нужно, элемент из списка, затем покидать секцию.

Любые другие извращения до добра не доведут :)


s_синхронизатор синхронизатор;

read()
{
   синхронизатор.lock();
   {
       шустрые действия
   }
   синхронизатор.unlock();
}

write()
{
   синхронизатор.lock();
   {
       шустрые действия
   }
   синхронизатор.unlock();
}
Iron Bug
это так называемый UpgradeLockable Concept.
есть расшаренные мьютексы, которые могут быть "проапгрейджены" до монопольного владения.
если никто не заявил право на монополию, то все пользуются ресурсом. если кто-то захватил монополию - никто не имеет доступ до освобождения монополии.
подробно читать тут:
http://live.boost.org/doc/libs/1_44_0/doc/...pgrade_lockable
Mifodix
Цитата(Iron Bug @ 28.5.2011, 15:06) *
это так называемый UpgradeLockable Concept.
есть расшаренные мьютексы, которые могут быть "проапгрейджены" до монопольного владения.
если никто не заявил право на монополию, то все пользуются ресурсом. если кто-то захватил монополию - никто не имеет доступ до освобождения монополии.
подробно читать тут:
http://live.boost.org/doc/libs/1_44_0/doc/...pgrade_lockable

Большое спасибо за ссылку, однако я не смог самостоятельно разобраться:( Следующий код падает:
#include <boost/thread.hpp>
#include <list>
#include <iostream>

boost::shared_mutex shmutex;
std::list<int> l;

void thread_func_read(int num)
{
    boost::shared_lock<boost::shared_mutex> readLock(shmutex);
    readLock.lock();
    std::cout<<"Thread read "<<num<< " Data: ";
    for (std::list<int>::iterator it=l.begin(); it!=l.end(); ++it)
    {
        std::cout<<*it<<" ";
    }
    std::cout<<std::endl;
    sleep(5);
}

void thread_func_write(int num)
{
    boost::upgrade_lock<boost::shared_mutex> upgradeLock(shmutex);
    boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(upgradeLock);
    std::cout<<"Thread write "<<num<< " Data: ";
    l.push_front(100);
    std::cout<<std::endl;
    sleep(5);
}

int main()
{
    boost::thread_group thrds;
    l.push_back(10);
    l.push_back(20);
    l.push_back(30);
    l.push_back(40);
    thrds.add_thread(new boost::thread(thread_func_read, 1));
    thrds.add_thread(new boost::thread(thread_func_read, 2));
    thrds.add_thread(new boost::thread(thread_func_read, 3));
    thrds.add_thread(new boost::thread(thread_func_write, 4));
    thrds.add_thread(new boost::thread(thread_func_write, 5));
    thrds.join_all();
    return 0;
}

Не понимаю как апгрейдить блокировку на запись только с одного потока.
Iron Bug
у тебя поток на чтение захватывает лок сразу же после создания объекта read_lock и дополнительно лочить его не требуется: попытка повторного лока приведёт к экспешену. после создания объекта shared_lock можно только разлочивать его и потом, по необходимости, снова лочить и так далее.
не надо торопиться. внимательнее читай документацию и смотри описания postcondition в функциях темплейтов для разных видов локов. плюс в бусте много примеров к каждой библиотеке.
Mifodix
Цитата(Iron Bug @ 28.5.2011, 22:27) *
у тебя поток на чтение захватывает лок сразу же после создания объекта read_lock и дополнительно лочить его не требуется: попытка повторного лока приведёт к экспешену. после создания объекта shared_lock можно только разлочивать его и потом, по необходимости, снова лочить и так далее.
не надо торопиться. внимательнее читай документацию и смотри описания postcondition в функциях темплейтов для разных видов локов. плюс в бусте много примеров к каждой библиотеке.

Вот с примерами как раз дело плохо:)
Более-менее разобрался:
#include <boost/thread.hpp>
#include <list>
#include <iostream>

boost::shared_mutex shmutex;
std::list<int> l;

void thread_func_read(int num)
{
    shmutex.lock_shared();
    std::cout<<"Thread read "<<num<< " Data: ";
    for (std::list<int>::iterator it=l.begin(); it!=l.end(); ++it)
    {
        std::cout<<*it<<" ";
    }
    std::cout<<std::endl;
    sleep(5);
    shmutex.unlock_shared();
}

void thread_func_write(int num)
{
    shmutex.lock_upgrade();
    std::cout<<"Thread write "<<num<<std::endl;
    l.push_front(100);
    sleep(5);
    shmutex.unlock_upgrade();
}

int main()
{
    boost::thread_group thrds;
    l.push_back(10);
    l.push_back(20);
    l.push_back(30);
    l.push_back(40);
    thrds.add_thread(new boost::thread(thread_func_read, 1));
    thrds.add_thread(new boost::thread(thread_func_read, 2));
    thrds.add_thread(new boost::thread(thread_func_read, 3));
    thrds.add_thread(new boost::thread(thread_func_write, 4));
    thrds.add_thread(new boost::thread(thread_func_write, 5));
    thrds.join_all();
    return 0;
}


Читающие потоки действительно читают одновременно, а пишущие потоки ждут пока завершится читающий поток и ждут завершения друг друга (т.е. одновременно не пишут). Но вот в чём бага: если сначала выполняется пишущий поток, то читающий поток почему-то не ждёт его завершения, а начинает читать одновременно. Где я ошибся?
Iron Bug
void thread_func_write(int num)
{
    shmutex.lock_upgrade();
    shmutex.unlock_upgrade_and_lock();
    std::cout<<"Thread write "<<num<<std::endl;
    l.push_front(100);
    sleep(5);
    shmutex.unlock_and_lock_upgrade();
    shmutex.unlock_upgrade();
}

сам по себе апгрейд ещё не есть захват эксклюзивного права на пользование ресурсом, а только заявка на то, что когда-то потом поток будет иметь право на такой захват. чтобы получить эксклюзивное - нужно его залочить.
(см. exclusive ownership в доках к бусту)
Mifodix
Цитата(Iron Bug @ 29.5.2011, 13:24) *
void thread_func_write(int num)
{
    shmutex.lock_upgrade();
    shmutex.unlock_upgrade_and_lock();
    std::cout<<"Thread write "<<num<<std::endl;
    l.push_front(100);
    sleep(5);
    shmutex.unlock_and_lock_upgrade();
    shmutex.unlock_upgrade();
}

сам по себе апгрейд ещё не есть захват эксклюзивного права на пользование ресурсом, а только заявка на то, что когда-то потом поток будет иметь право на такой захват. чтобы получить эксклюзивное - нужно его залочить.
(см. exclusive ownership в доках к бусту)


Хех, главное я сначала сделал unlock_upgrade_and_lock() + unlock_and_lock_upgrade(), потом lock_upgrade()+unlock_upgrade(), но вот вместе их использовать не догадался:) Большое вам спасибо за помощь! Почитаю ещё доки и попробую использовать эту концепцию в реальном проекте:)
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.