2015-06-23 2 views
1

Я экспериментирую с потоками Boost, поскольку, насколько мне известно, я могу написать многопоточное приложение Boost и скомпилировать его в Windows или Linux, а pthreads, что я более знакомо с, строго используется для систем * NIX.Boost w/C++ - Любопытное поведение мьютекса

Я следующий пример приложения, which is borrowed from another SO question:


#include <boost/thread/thread.hpp> 
#include <boost/thread/mutex.hpp> 
#include <boost/bind.hpp> 
#include <iostream> 

#define NAP_DURATION (10000UL) // 10ms 

boost::mutex io_mutex; 

void count(int id) 
{ 
    for (int i = 0; i < 1000; ++i) 
    { 
     boost::mutex::scoped_lock lock(io_mutex); 
     std::cout << "Thread ID:" << id << ": " << i << std::endl; 
     if (id == 1) 
     { 
      std::cout << "I'm thread " << id << " and I'm taking a short nap" << std::endl; 
      usleep(NAP_DURATION); 
     } 
     else 
     { 
      std::cout << "I'm thread " << id << ", I drink 100 cups of coffee and don't need a nap" << std::endl; 
     } 
     std::cout << "Thread ID:" << id << ": " << i << std::endl; 
     boost::thread::yield(); 
    } 
} 

int main(int argc, char* argv[]) 
{ 
    boost::thread thrd1(boost::bind(&count, 1)); 
    boost::thread thrd2(boost::bind(&count, 2)); 

    thrd1.join(); 
    thrd2.join(); 
    return 0; 
} 

я установил подталкивание на моем Ubuntu 14.04 системе LTS с помощью:

sudo apt-get install libboost-all-dev 

И я компилировать код выше через:

g++ test.cpp -lboost_system -lboost_thread -I"$BOOST_INLCUDE" -L"$BOOST_LIB" 

Я столкнулся с тем, что, как представляется, представляет собой некоторые интересные несоответствия. Если я установил длинный NAP_DURATION, скажем 1 секунду (1000000), кажется, что только нить 1 когда-либо получает мьютекс, пока не завершит свою работу, и очень редко происходит то, что нить 2 получает блокировку до тех пор, пока не закончится нить 1, даже если я установил NAP_DURATION - всего несколько миллисекунд.

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


Так, на вопрос (ы):

  1. Это ожидаемое поведение?
  2. Есть ли способ управлять этим поведением, например, вести себя как заблокированные блокировки, как операции блокировки?
  3. Если ответ на (2) «нет», возможно ли добиться чего-то подобного с переменными состояния Boost и не беспокоиться о сбоях блокировки/разблокировки?
  4. Действительно ли scoped_lock s гарантированно разблокировать? Я использую подход RAII, а не ручную блокировку/разблокировку, потому что, по-видимому, операция разблокировки может завершиться неудачей и вызвать исключение, и я пытаюсь сделать этот код прочным.

спасибо.

Разъяснения

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

  • thread1 блокирует, получает мьютекс.
  • Thread2 замки, блоки.
  • Thread1 выполняет, освобождает замок и сразу же пытается заблокировать его.
  • Thread2 уже ждал на замке, получает его перед thread1.
+0

Просто догадаться, но не 'usleep' заставляет родительский процесс спать? Возможно, попробуйте использовать 'boost :: this_thread :: sleep'. Странно, чтобы спальная нить удерживала замок. –

+0

@JohnnyCage Это распространенное заблуждение. 'usleep' останавливает только вызов * thread *. См. Обсуждение [здесь] (http://stackoverflow.com/questions/11915292/do-sleep-functions-sleep-all-threads-or-just-the-one-who-call-it). Однако верно, что 'this_thread :: sleep' предпочтительнее – Drop

+0

О, я знаю, что' sleep' просто приостанавливает вызывающий поток и не освобождает блокировку. Меня больше беспокоит планирование. Я бы ожидал, что поток 2, поскольку он уже блокирует блокировку, выполнит перед потоком 1, так как ему придется повторно захватить блокировку. – DevNull

ответ

5

Это ожидаемое поведение?

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

Есть ли способ контролировать это поведение, например, вести себя как заблокированные блокировки, как операции блокировки?

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

Если ответ на (2) «нет», возможно ли добиться чего-то подобного с переменными состояния Boost и не беспокоиться о сбоях блокировки/разблокировки?

Несомненно. Код, который вы хотите.

Гарантировано ли обнаружение scoped_locks для разблокировки? Я использую подход RAII, а не ручную блокировку/разблокировку, потому что, по-видимому, операция разблокировки может завершиться неудачей и вызвать исключение, и я пытаюсь сделать этот код прочным.

Непонятно, о чем вас беспокоит, но рекомендуется использовать подход RAII.

1

Почему вы удивлены, точно? Если вы ожидали, что поток 2 получит мьютекс, пока поток 1 спит, тогда да, это ожидает поведения, и ваше понимание было неправильным, потому что ваша блокировка находится в области видимости.

Но если вы удивлены из-за отсутствие альтернанса между резьбой 1 и резьбой 2 в конце итерации цикла, то вы можете посмотреть на this SO question о планировании, что кажется «несправедливым»

+0

Последняя часть о «несправедливом расписании» была тем, что я искал. Благодаря! – DevNull