crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

 
Ответить в данную темуНачать новую тему
> множественное чтение и единичная запись через мьютексы, shared_mutex, shared_lock, upgrade_lock
Mifodix
  опции профиля:
сообщение 27.5.2011, 22:25
Сообщение #1


Новичок


Группа: Новичок
Сообщений: 9
Регистрация: 7.6.2010
Пользователь №: 1789

Спасибо сказали: 0 раз(а)




Репутация:   0  


Всем доброго времени!
Чтобы проще описать мою проблему, рассмотрим следующую программу:
#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
  опции профиля:
сообщение 28.5.2011, 10:31
Сообщение #2


фрилансер
******

Группа: Участник
Сообщений: 2939
Регистрация: 19.6.2010
Из: Обливион
Пользователь №: 1822

Спасибо сказали: 215 раз(а)




Репутация:   34  


и чтение, и запись должны одинаково закрываться в "критическую секцию". При чтении нужно быстренько копировать элемент из списка во внешнюю (относительно секции) переменную, удалять, если это нужно, элемент из списка, затем покидать секцию.

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


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

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

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


Сообщение отредактировал Алексей1153 - 28.5.2011, 10:33
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 28.5.2011, 14:06
Сообщение #3


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


это так называемый UpgradeLockable Concept.
есть расшаренные мьютексы, которые могут быть "проапгрейджены" до монопольного владения.
если никто не заявил право на монополию, то все пользуются ресурсом. если кто-то захватил монополию - никто не имеет доступ до освобождения монополии.
подробно читать тут:
http://live.boost.org/doc/libs/1_44_0/doc/...pgrade_lockable
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Mifodix
  опции профиля:
сообщение 28.5.2011, 19:33
Сообщение #4


Новичок


Группа: Новичок
Сообщений: 9
Регистрация: 7.6.2010
Пользователь №: 1789

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(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
  опции профиля:
сообщение 28.5.2011, 21:27
Сообщение #5


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


у тебя поток на чтение захватывает лок сразу же после создания объекта read_lock и дополнительно лочить его не требуется: попытка повторного лока приведёт к экспешену. после создания объекта shared_lock можно только разлочивать его и потом, по необходимости, снова лочить и так далее.
не надо торопиться. внимательнее читай документацию и смотри описания postcondition в функциях темплейтов для разных видов локов. плюс в бусте много примеров к каждой библиотеке.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Mifodix
  опции профиля:
сообщение 29.5.2011, 0:06
Сообщение #6


Новичок


Группа: Новичок
Сообщений: 9
Регистрация: 7.6.2010
Пользователь №: 1789

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(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
  опции профиля:
сообщение 29.5.2011, 12:24
Сообщение #7


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


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 в доках к бусту)

Сообщение отредактировал Iron Bug - 29.5.2011, 12:25
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Mifodix
  опции профиля:
сообщение 29.5.2011, 17:51
Сообщение #8


Новичок


Группа: Новичок
Сообщений: 9
Регистрация: 7.6.2010
Пользователь №: 1789

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(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(), но вот вместе их использовать не догадался:) Большое вам спасибо за помощь! Почитаю ещё доки и попробую использовать эту концепцию в реальном проекте:)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 29.4.2024, 20:03