Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






  • Void f()






    {

    //...

    // создаемблокировку, но не захватываем мьютекс

    std:: unique_lock lck(m, std:: defer_lock);

    //...

    if (lck.try_lock()) {

    // работа с разделяемыми ресурсами:

    sh+=1;

    }

    else {

    // возможно делаем что-то еще

    }

    }

     

    Аналогично, unique_lock поддерживает методы try_lock_for() и try_lock_until(). Использование блокировок по сравнению с ручным использованием мьютекса дает безопасность с точки зрения исключений, и не дает забыть вызвать метод unlock(). В параллельном программировании нам пригодится любая помощь.

    А что если нам нужны два ресурса, представленные двумя мьютексами? Простой способ заключается в захвате двух мьютексов по очереди:

    std:: mutex m1;

    std:: mutex m2;

    int sh1; // разделяемые данные

    Int sh2

    //...

    Void f()

    {

    //...

    std:: unique_lock lck1(m1);

    std:: unique_lock lck2(m2);

    // работа с разделяемыми ресурсами:

    sh1+=sh2;

    }

     

    Этот код может привести к серьезной проблеме, если другой поток попытается захватить m1 и m2 в противоположном порядке, тогда каждый из них будет держать блокировку, необходимую другому потоку для продолжения своей работы, в результате они будут ожидать друг друга вечно (это называется взаимоблокировкой (deadlock)). При большом количестве блокировок в системе это становится реальной опасностью. Именно поэтому стандартные блокировки предоставляют две функции для безопасной попытки захвата двух или более блокировок:

    Void f()

    {

    //...

    // создаем блокировки, но пока не захватываем мьютексы

    std:: unique_lock lck1(m1, std:: defer_lock);

    std:: unique_lock lck2(m2, std:: defer_lock);

    std:: unique_lock lck3(m3, std:: defer_lock);

    Lock(lck1, lck2, lck3);

    // работа с разделяемыми данными

    }

     

    Очевидно, реализация lock() должна быть тщательно продумана, чтобы избежать взаимоблокировок. По сути, это эквивалентно использованию нескольких вызовов try_lock(). Если во время lock() не удастся захватить ни одной блокировки, то будет сгенерировано исключение. На самом деле, метод lock() может принимать любые аргументы, которые содержат методы lock(), try_lock() и unlock() (как, например, класс mutex), поэтому мы не можем точно говорить о том, какие исключения может генерировать метод lock(); это зависит от его аргументов.

    Если вы предпочитаете использовать try_lock(), то вам может помочь метод, эквивалентный методу lock():

    Void f()

    {

    //...

    // создаем блокировки, но пока не захватываем мьютексы

    std:: unique_lock lck1(m1, std:: defer_lock);

    std:: unique_lock lck2(m2, std:: defer_lock);

    std:: unique_lock lck3(m3, std:: defer_lock);

    Int x;

    // добро пожаловать в мир C

    if ((x = try_lock(lck1, lck2, lck3))==-1) {

    // работаем с разделяемыми данными

    }

    else {

    // x содержит индекс мьютекса, который не удалось захватить

    // например, если lck2.try_lock() завершится неудачно, то x==1

    }

    }

     

    См. также:

    • Standard: 30.4.3 Locks [thread.lock]
    • ???

     






    © 2023 :: MyLektsii.ru :: Мои Лекции
    Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
    Копирование текстов разрешено только с указанием индексируемой ссылки на источник.