Поток безопасности против входа - PullRequest
4 голосов
/ 25 февраля 2012

Я знаю, что это много обсуждалось на этом форуме.Но одна вещь все еще смущает меня.В Википедии упоминается, что каждый поступающий код является потокобезопасным.http://en.wikipedia.org/wiki/Reentrant_%28subroutine%29 А позже приведу пример функции, которая является входящей, но не является поточно-ориентированной.Все повторно поступающие коды безопасны для потока?Кроме того, все рекурсивные функции потокобезопасны.Я не могу представить разницу.

Спасибо

Ответы [ 5 ]

2 голосов
/ 25 февраля 2012

Понятия повторного входа и безопасности потока связаны, но не эквивалентны.Вы можете написать реентерабельную функцию, которая не является поточно-ориентированной, и поточно-безопасную функцию, которая не является реентерабельной.Я буду использовать C # для моих примеров:

Функция повторного входа, которая не является поточно-ориентированной

Эта функция переворачивает записи массива:

void Reverse(int[] data) {
    if (data == null || data.Length < 2) return;
    for (var i = 0 ; i != data.Length/2 ; i++) {
        int tmp = data[i];
        data[i] = data[data.Length-i-1];
        data[data.Length-i-1] = tmp;
    }
}

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

void Reverse(int[] data) {
    if (data == null || data.Length < 2) return;
    lock (data) {
        for (var i = 0 ; i != data.Length/2 ; i++) {
            int tmp = data[i];
            data[i] = data[data.Length-i-1];
            data[data.Length-i-1] = tmp;
        }
    }
}

Поточно-ориентированная функция, которая не является повторным входом

Thisфункция вызывает функцию f() c раз и возвращает значение, которое было возвращено больше раз, чем другие значения.

static int[] counts = new int[65536];

unsigned short MaxCount(Func<unsigned short> f, int c) {
    lock(counts) {
        Array.Clear(counts, 0, counts.Length);
        for (var i = 0 ; i != c ; i++) {
            counts[f()]++;
        }
        unsigned short res = 0;
        for (var i = 1 ; i != counts.Length ; i++) {
            if (counts[i] > counts[res]) {
                res = i;
            }
        }
        return res;
    }
}

Эта функция является поточно-ориентированной, поскольку она блокирует используемый статический массивсделать подсчет.Однако, это не повторный вход: например, если переданный функтор f должен был вызвать MaxCount, будут возвращены неверные результаты.

1 голос
/ 25 февраля 2012

Являются ли все повторно поступающие коды безопасными для потоков?

Нет.

Далее в статье в Википедии: … Ключом для избежания путаницы являетсяэтот реентрант относится только к выполнению одного потока.Это концепция того времени, когда не существовало многозадачных операционных систем.

Другими словами, концепция повторного входа не учитывает многопоточное выполнение (как наиболее распространенный пример).Только если это фактическое ограничение в вашей системе, тогда да, повторное поступление будет считаться потокобезопасным (, но в этой среде также будет только один поток).

IMO, термин«reentrant» следует использовать только в контексте соответствующих систем (не относится к вашей настольной ОС, но будет применяться к некоторым встроенным системам).

Теперь, если вы действительно хотитепринудительное использование слова 'reentrance' в многопоточной среде: вы можете принудительно использовать reentrance в многопоточной системе, только если вы также гарантируете, что она также является поточно-ориентированной.Возможно, лучше задать вопрос: «Чтобы гарантировать повторный вход в многопоточном контексте, подразумевается ли это, что функция и все данные, на которые она ссылается, также должны быть поточно-безопасными?» - A: Да, функцияи все, на что он ссылается, также должно быть потокобезопасным и реентерабельным, чтобы функция реентерабливалась в этом контексте.Достижение этого быстро становится сложным, и это причина, по которой глобальные переменные не очень хорошая идея.

Кроме того, все рекурсивные функции безопасны для потоков.

Нет.

0 голосов
/ 25 февраля 2012

Функция «swap» не является реентерабельной, поскольку использует глобальное состояние (для глобальной переменной «t»).

0 голосов
/ 25 февраля 2012

Если ваша подпрограмма не влияет на внешние переменные (без побочных эффектов), вы можете сказать, что является входящим .Хотя это не практическое правило, это хорошее руководство.
Если подпрограмма использует внешние переменные и сохраняет состояние внешних переменных в начале и восстанавливает это состояние в конце, тогда подпрограмма повторноабитуриент .

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

Повторно входящие функции сохраняют свое состояние (в своем локальном стеке, в стеке потока, не используя глобальные переменные).

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

Обычно Потоковая безопасность подразумевает повторное поступление, но опять же не правило, больше ориентир. Примечание: Некоторые блокировки в java являются реентерабельными, поэтому вы можете рекурсивно вызывать метод и не блокироваться предыдущими вызовами.(повторный вход в этом контексте означает, что потоки могут получить доступ к любому разделу, заблокированному с той же блокировкой - поток может повторно ввести любой блок кода, для которого он уже удерживает блокировку)

Решение / Ответ:
Если вы защищаете t с помощью атомарного режима, вы должны получить подпрограмму с защитой потоков.Кроме того, если вы поместите t в стек каждого потока (сделайте его локальным), тогда подпрограмма станет потокобезопасной, поскольку глобальных данных нет.

Кроме того, reentrant! = Recursive;вы также можете выполнить ISR в рекурсивной подпрограмме.Потоково, если вы вызовете рекурсивную подпрограмму из 2 потоков, вы получите мусор.Чтобы сделать потокобезопасным, защитите рекурсивную подпрограмму с помощью повторяющихся блокировок (другие не повторяющиеся блокировки приведут к взаимоблокировке / блокировке).

0 голосов
/ 25 февраля 2012

Эта функция не является поточно-ориентированной, поскольку она обращается к ресурсу t, который находится за пределами области действия функции (не выделен в стеке) без каких-либо механизмов защиты (например, блокировки), чтобы гарантировать атомарный доступ к t.

На странице Википедии написано:

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

(мой акцент).

UPDATE:

Reentrant (как определено в статье) просто означает, что один поток выполнения (например, DOS) может «вырвать указатель инструкции» из середины выполняемой подпрограммы, поместить его в другое место и продолжить линейный поток через новый код (например, подпрограмма прерывания в дни DOS), и тот же линейный поток может вернуться во второй раз. В этом ограниченном сценарии второй вызов функции ДОЛЖЕН завершиться до того, как управление будет перенесено из подпрограммы прерывания обратно в «обычное» выполнение программы, которое возобновится в той же самой точке подпрограммы, где указатель инструкции был первоначально разорван. Этот сценарий допускает не произвольное планирование потоков, а прерывание, которое переключает управление на новую точку, которая затем завершается и возобновляет работу в том месте, где оно было прервано.

Обратите внимание, что в реальной жизни все может быть не так просто. Я больше не совсем уверен (было ... 20 лет?), Но я думаю, что одна подпрограмма прерывания может прерывать и прерывать подпрограмму, которая уже выполняется (например, прерывание мягкого отладчика может прерывать прерывание по таймеру и т. Д.).

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