2013-10-03 3 views
2

Я хочу написать пользовательский мьютекс, чтобы каждый поток мог предоставить аргумент, представляющий сложность операций, которые текущий поток хочет выполнить. Если сложность операции низкая, другие потоки будут находиться в цикле, таком как блокировка спина, но если сложность операции будет средней, каждый поток будет итерации 50 раз, а затем будет спящий по переменной условия, и если операция очень сложна, другие потоки будут отправляйтесь спать прямо.Способы включения потоков в спиновые блокировки

enum LockOperation 
    { 
     LockOperation_Light = -1, 
     LockOperation_Medium = 50, 
     LockOperation_Heavy = 1 
    }; 

    class CustomMutex 
    { 
    private: 

    protected: 
     std::atomic<int> mFlag; 
     std::mutex mMutex; 
     std::condition_variable mCond; 

    public: 
     CustomMutex() { std::atomic_init(&mFlag, 0); }; 
     ~CustomMutex() {}; 

     void lock(LockOperation pLockOperation = LockOperation_Medium) 
     { 
      int lNewLoopCount = static_cast<int>(pLockOperation); 
      int lLoopCounter = 0; 
      int lExpected = 0; 
      int lLoopCount = std::atomic_load_explicit(&mFlag, std::memory_order_relaxed); 

      while (true) 
      { 
       while(std::atomic_load_explicit(&mFlag, std::memory_order_relaxed) != 0 && 
         lLoopCounter != lLoopCount) 
        ++lLoopCounter; 
       std::atomic_compare_exchange_strong_explicit(
        &mFlag, 
        &lExpected, 
        lNewLoopCount, 
        std::memory_order_acquire, 
        std::memory_order_relaxed); 
       if(lExpected == 0) 
       { 
        return; 
       } 
       else if(lLoopCounter == lLoopCount) 
       { 
        lExpected = 0; 
        std::unique_lock<std::mutex> lGuard(mMutex); 
        mCond.wait(lGuard); 
       } 
       else 
       { 
        lExpected = 0; 
        continue; 
       } 
      } 
     }; 
     bcInline void UnLock() 
     { 
      std::atomic_store_explicit(&mFlag, 0, std::memory_order_relaxed); 
      std::lock_guard<std::mutex> lGuard(mMutex); 
      mCond.notify_one(); 
     }; 
    }; 

Предположит теперь thread1 замки этого мьютекс и thread2 идет для ожидания из-за его достижением его счётчик цикла конца и прямо перед блокированием мьютекса при условии переменного, thread1 вызовы оповещать о переменном состоянии. Теперь thread2 будет спать, пока другой поток не заблокирует этот мьютекс, а затем вызовет разблокировку.

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

Другой вопрос: Правильно ли упорядочен порядок работы с атомами?

(извините, если мой вопрос как-то неоднозначное, мой английский не очень хорошо)

+0

Спин-блокировки - плохая идея в пользовательском пространстве. В большинстве хорошо спроектированных приложений утверждение mutex на удивление редко встречается - и путь кода для «незащищенного» блокирования обычно оптимизирован. Классификация операций как «свет», «средний» или «тяжелый» является одной из тех вещей, которые заставляют меня думать о проблемах [приоритетной инверсии] (http://en.wikipedia.org/wiki/Priority_inversion). –

+0

Я должен согласиться с @BrettHale :( –

+0

@BrettHale Хорошо, но я думаю, что проблема приоритета инверсии в моем примере не встречается. Легкие, средние и тяжелые - это только флаги, владельцы мьютексов сообщают ожидающим потокам, сколько они должны зацикливаться до вызывая дорогостоящие системные вызовы.Я думаю, что нет никакого приоритетного спора между легкими и тяжелыми операциями, и получение мьютекса все еще происходит случайным образом. Почему вы говорите, что блокировка спина - это плохая идея? в «легких операциях», например, добавление или удаление элементов из связанного списка это будет хорошая идея, что мы позволяем потокам спина вместо блокировки вызова на мьютексе. (Я не понимаю, что «Замки спина - плохая идея в пользовательском пространстве») – MRB

ответ

0

Я думаю, что если я изменить второе, если это:

else if(lLoopCounter == lLoopCount) 
{ 
    lExpected = 0; 
    std::unique_lock<std::mutex> lGuard(mMutex); 
    mCond.wait(
     lGuard, 
     [&]()->bool { return std::atomic_load_explicit(&mFlag, std::memory_order_relaxed) == 0; }); 
} 

и разблокировать это:

std::lock_guard<std::mutex> lGuard(mMutex); 
std::atomic_store_explicit(&mFlag, 0, std::memory_order_relaxed); 
mCond.notify_one(); 

проблема решит

Теперь для оптимизации я могу удалить lGuard из метода разблокировки a nd change std :: memory_order_relaxed to std :: memory_order_release? (У меня были сомнения, чтобы задать новый вопрос или нет)

Смежные вопросы