простой нестандартный мьютекс - PullRequest
3 голосов
/ 28 апреля 2011

Можете ли вы определить ошибку в коде?билеты заканчивают тем, что становятся ниже 0, вызывая длинные остановки.

struct SContext {
    volatile unsigned long* mutex;
    volatile long* ticket;
    volatile bool* done;
};

static unsigned int MyThreadFunc(SContext* ctxt) {

    // -- keep going until we signal for thread to close
    while(*ctxt->done == false) {

        while(*ctxt->ticket) { // while we have tickets waiting
            unsigned int lockedaquired = 0;
            do {
                if(*ctxt->mutex == 0) { // only try if someone doesn't have mutex locked
                    // -- if the compare and swap doesn't work then the function returns
                    // -- the value it expects
                    lockedaquired = InterlockedCompareExchange(ctxt->mutex, 1, 0);
                }
            } while(lockedaquired !=  0); // loop while we didn't aquire lock
            // -- enter critical section

            // -- grab a ticket
            if(*ctxt->ticket > 0);
                     (*ctxt->ticket)--;

            // -- exit critical section
            *ctxt->mutex = 0; // release lock
        }
     }

     return 0;
}

Вызов функции, ожидающей окончания потоков

    for(unsigned int loops = 0; loops < eLoopCount; ++loops) {
        *ctxt.ticket = eNumThreads; // let the threads start!

        // -- wait for threads to finish
        while(*ctxt.ticket != 0)
            ; 
    }
    done = true;

РЕДАКТИРОВАТЬ:

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

Я инициализирую lockaquired равным 0. Затем в качестве оптимизации, чтобы не задействовать полосу пропускания шины, я не выполняю CAS, если мьютекс взят.

К сожалению, в этом случае, когдаблокировка снята, а цикл while пропустит второй поток!

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

Ответы [ 2 ]

1 голос
/ 28 апреля 2011

Я вижу другую гонку в вашем коде: один поток может привести к тому, что *ctxt.ticket достигнет 0, что позволит родительскому циклу вернуться назад и переустановить *ctxt.ticket = eNumThreads, не удерживая *ctxt.mutex.Какой-то другой поток может теперь уже удерживать мьютекс (на самом деле, вероятно, он это делает) и работать на *ctxt.ticket.Для вашего упрощенного примера это только предотвращает чистое разделение «пакетов», но если у вас была более сложная инициализация (например, при более сложной записи, чем запись одного слова) в верхней части цикла loops, вы можете увидеть странное поведение.

0 голосов
/ 28 апреля 2011

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

unsigned int lockedaquired = 0;

Я инициализировал lockaquired равным 0, а затем после того, как добавил оператор if, чтобы пропустить дорогостоящую операцию выполнения CAS.Эта оптимизация вызвала его выпадение из цикла while в критическую секцию.Изменение кода на

unsigned int lockedaquired = 1;

Устраняет проблему.В коде, который я обнаружил, есть еще одна скрытая проблема (я действительно больше не должен кодировать поздно ночью).Кто-нибудь замечает точку с запятой после оператора if в критическом разделе?Вздох ...

if(*ctxt->ticket > 0);
    (*ctxt->ticket)--;

Это должно быть

if(*ctxt->ticket > 0)

Кроме того, Бен Джексон указал, что поток, вероятно, будет в критической секции, когда мы сбросим тикет в eNumThreads.Хотя это прекрасно в этом примере кода, если вы примените его к проблеме, где вам нужно было выполнять больше операций, это может быть небезопасно, потому что потоки не работают в режиме ожидания, так что имейте это в виду, если вы примените это к своемуcode.

Последнее замечание: если кто-то решит использовать этот код для собственной реализации мьютекса, помните, что ваш основной поток драйверов вращается вхолостую.Если вы выполняете большую работу в критической секции, которая занимает много времени, а количество билетов очень велико, подумайте о том, чтобы уступить потоку, чтобы другое программное обеспечение могло использовать ЦП во время его ожидания.Кроме того, рассмотрите возможность использования спин-блокировки, если критическая секция велика.

Спасибо

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