Каков наилучший способ ожидания переменной в многопоточном приложении? - PullRequest
9 голосов
/ 13 января 2010

Я хотел бы сделать что-то подобное ниже для многопоточной программы:

// wait for variable to become true but don't hog resources  
// then re-sync queues  

Является ли чем-то вроде этого хорошим решением?

while (!ready) {
    Thread.Sleep(250); // pause for 1/4 second;
};

Ответы [ 8 ]

15 голосов
/ 14 января 2010

Нет, это не очень хорошее решение. Сначала это может спать слишком долго. Во-вторых, потокам легко попасть в тупик. Вот несколько ссылок на статьи MSDN о правильной технике синхронизации:

5 голосов
/ 14 января 2010

Вот как вы делаете это с помощью boost:

boost::condition_variable condvar;
boost::mutex mutex;
bool finished1 = false;
bool finished2 = false;

void longComputation1()
{
    {
        boost::lock_guard<boost::mutex> lock(mutex);
        finished1 = false;
    }
    // Perform long computation
    {
        boost::lock_guard<boost::mutex> lock(mutex);
        finished1 = true;
    }
    condvar.notify_one();
}

void longComputation2()
{
    {
        boost::lock_guard<boost::mutex> lock(mutex);
        finished2 = false;
    }
    // Perform long computation
    {
        boost::lock_guard<boost::mutex> lock(mutex);
        finished2 = true;
    }
    condvar.notify_one();
}

void somefunction()
{
    // Wait for long computations to finish without "spinning"
    boost::lock_guard<boost::mutex> lock(mutex);
    while(!finished1 && !finished2)
    {
        condvar.wait(lock);
    }

    // Computations are finished
}

Ради краткости я не включил код порождения потока.

boost::lock_guard использует идиому RAII для автоматической разблокировки мьютекса, когда объект блокировки выходит из области видимости. Очень полезно для предотвращения взаимных блокировок в случае исключений.

Я считаю, что условные переменные менее подвержены ошибкам, чем объекты Microsoft. Если вы используете boost.Thread, вы получите дополнительное преимущество кроссплатформенной мобильности.

4 голосов
/ 14 января 2010

Попробуйте использовать Событие (объект ядра) вместо простой переменной и замените ваш цикл на:

WaitForSingleObject(hEventHandle, INFINITE);
1 голос
/ 14 января 2010

Код выше будет работать, и, возможно, уместно в некоторых обстоятельствах.

Вы также можете посмотреть критический раздел или семафор - это сделает ваше приложение блокированным и подождет, пока ресурс станет доступным,

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

0 голосов
/ 14 января 2010

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

WaitForSingleObject для дескриптора события Win32 позволяет вам обнаруживать необходимый сигнал псевдо-немедленно (в зависимости от того, что делают другие потоки в вашем процессе) и не выполнять избыточных проверок (сколько ненужных циклов нужно выполнить перед сигналом приходит?), при условии, что поток настроек вызывает SetEvent, как только он закончил свою работу. Тогда bool становится лишним, как и должно быть.

0 голосов
/ 14 января 2010

В сыром Win32 API есть СОБЫТИЕ для этого, вот пример использования:

http://msdn.microsoft.com/en-us/library/ms686915(VS.85).aspx

Однако этот API ориентирован на C и относится к Windows. Если вы пишете программу на C ++, вы можете подумать о том, чтобы сделать свой код более независимым от платформы, используя что-то вроде boost :: threads, аналог которого есть в Условия .

Предупреждение, которое я обнаружил, заключается в том, что Windows может WaitForMultipleObjects , таким образом ожидая одновременно несколько событий (и других классов дескрипторов). буст не имеет параллельного AFAIK.

0 голосов
/ 14 января 2010

Конечно, это C #, но я нашел эту книгу чрезвычайно полезной для разработки многопоточности.

http://www.albahari.com/threading/

Некоторая информация не зависит от языка.

0 голосов
/ 14 января 2010

Прежде всего, вам нужно объявить переменную 'ready' как минимум 'volatile', иначе это может иметь неприятные побочные эффекты. Во-вторых, сон , который долго, прежде чем переоценивать состояние, является хорошей идеей, если продолжительность, которую он может занять, действительно очень велика, скажем, несколько минут.

Использование функций Event в WinAPI (CreateEvent, SetEvent(), WaitForSingleEvent()) - лучший способ сделать это. Конечно, это вносит некоторые накладные расходы, но обычно это нормально.

Если вы хотите придерживаться своего решения, зацикливание и перепроверка условия перед повторным сном может повысить производительность в некоторых сценариях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...