Типичные ошибки связанные с использованием блокировки
Попытка снять блокировку не из того потока, который ее установил
Если блокировка установлена одним потоком, попытка ее снятия из другого потока может привести к непредсказуемым результатам. Рассмотрим пример:
Некорректно:
omp_lock_t myLock; omp_init_lock(&myLock); #pragma omp parallel sections { #pragma omp section { ... omp_set_lock(&myLock); ... } #pragma omp section { ... omp_unset_lock(&myLock); ... } } |
В Си++ этот код приводит к ошибке во время выполнения программы. Поскольку операции установки и снятия блокировки аналогичны входу в критическую секцию и выходу из нее, каждый из использующих блокировку потоков должен выполнять обе операции. Корректная версия кода будет выглядеть следующим образом:
Корректно:
omp_lock_t myLock; omp_init_lock(&myLock); #pragma omp parallel sections { #pragma omp section { ... omp_set_lock(&myLock); ... omp_unset_lock(&myLock); ... } #pragma omp section { ... omp_set_lock(&myLock); ... omp_unset_lock(&myLock); ... } } |
Попытка использования блокировки как барьера
Функция omp_set_lock блокирует выполнение потока до тех пор, пока переменная, использующаяся для блокировки, не станет доступна, то есть до тех пор, пока тот же самый поток не вызовет функцию omp_unset_lock. Следовательно, как уже говорилось в описании предыдущей ошибки, каждый поток должен содержать вызовы обеих функций. Однако, начинающий программист, недостаточно хорошо понимающий принципы работы OpenMP, может попытаться использовать функцию omp_set_lock в качестве барьера, то есть вместо директивы #pragma omp barrier (эту директиву нельзя использовать внутри параллельных секций, к которым применяется директива #pragma omp sections). В результате возникнет следующий код.
Некорректно:
omp_lock_t myLock;omp_init_lock(&myLock);#pragma omp parallel sections{ #pragma omp section { ... omp_set_lock(&myLock); ... } #pragma omp section { ... omp_set_lock(&myLock); omp_unset_lock(&myLock); ... }} |
В результате выполнения этого фрагмента кода программа иногда будет зависать, а иногда – выполняться нормально. Зависеть это будет от того, какой поток завершается последним. Если последним будет завершаться поток, в котором выполняется блокировка переменной без ее освобождения, программа будет выдавать ожидаемый результат. Во всех остальных случаях будет возникать бесконечное ожидание освобождения переменной, захваченной потоком, работающим с переменной некорректно. Аналогичная ситуация будет возникать и при использовании функции omp_test_lock в цикле (а именно так эту функцию обычно и используют). В этом случае поток будет бесконечно «топтаться на месте», так и не дождавшись освобождения переменной.
Поскольку данная ошибка аналогична предыдущей, корректный вариант кода будет выглядеть так же.
Корректно:
omp_lock_t myLock;omp_init_lock(&myLock);#pragma omp parallel sections{ #pragma omp section { ... omp_set_lock(&myLock); ... omp_unset_lock(&myLock); ... } #pragma omp section { ... omp_set_lock(&myLock); ... omp_unset_lock(&myLock); ... }} |
Дата добавления: 2015-02-03; просмотров: 769;