Каковы преимущества локальной памяти потока уровня экземпляра?

Этот вопрос привел меня задаваться вопросом о локальной памяти потока в высокоуровневых платформах разработки как Java и.NET.

Java имеет a ThreadLocal класс (и возможно другие конструкции), в то время как.NET имеет слоты данных, и скоро a ThreadLocal собственный класс. (Это также имеет ThreadStaticAttribute, но я особенно интересуюсь локальной памятью потока для членских данных.) Большинство других современных сред разработки обеспечивает один или несколько механизмов для него, или в языке или в уровне платформы.

Какие проблемы локальная память потока решает, или какие преимущества локальная память потока обеспечивает по стандартной объектно-ориентированной идиоме создания экземпляров отдельного объекта для содержания локальных данных потока? Другими словами, как это:

// Thread local storage approach - start 200 threads using the same object
// Each thread creates a copy of any thread-local data
ThreadLocalInstance instance = new ThreadLocalInstance();
for(int i=0; i < 200; i++) {
    ThreadStart threadStart = new ThreadStart(instance.DoSomething);
    new Thread(threadStart).Start();
}

Выше этого?

// Normal oo approach, create 200 objects, start a new thread on each
for(int i=0; i < 200; i++) {
    StandardInstance standardInstance = new StandardInstance();
    ThreadStart threadStart = new ThreadStart(standardInstance.DoSomething);      
    new Thread(threadStart).Start();
}

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

17
задан Community 23 May 2017 в 12:25
поделиться

5 ответов

Какие проблемы решает локальное хранилище потока или какие преимущества дает локальное хранилище потока по сравнению со стандартной объектно-ориентированной идиомой создания отдельных экземпляров объекта для хранения локальных данных потока?

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

Что касается преимущества по сравнению с вашим примером - если вы создаете один поток, то преимущество использования локального хранилища потока перед передачей в экземпляре практически отсутствует. ThreadLocal и подобные конструкции становятся невероятно ценными, однако при работе (прямо или косвенно) с ThreadPool.

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

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

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

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

Для более конкретного примера взгляните на это сообщение в блоге, которое я написал об агрегировании с использованием TPL . Внутри класс Parallel использует ThreadLocal всякий раз, когда вы используете перегрузку ForEach, которая сохраняет локальное состояние (и методы Parallel.For , тоже). Таким образом, локальное состояние сохраняется отдельно для каждого потока, чтобы избежать блокировки.

11
ответ дан 30 November 2019 в 13:05
поделиться

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

Если требуется выполнить сортировку с помощью оператора, отличного от оператора < , способ, который вы описываете, является единственным способом сделать это в текущей версии C++ (хотя компаратор может быть просто обычной функцией; это не обязательно должен быть функтор). Стандарт C++ 0x сделает это менее подробным, разрешив лямбда-функции .

Если вы не хотите ждать C++ 0x, альтернативой является использование boost:: лямбда .

-121--1611661-

Я не думаю, что вы можете это сделать. jQuery animate работает только с любым «числовым свойством CSS».

-121--2970353-

Иногда полезно иметь локальное состояние потока. Одним из примеров является контекст журнала - может быть полезно задать контекст, какой запрос вы обслуживаете в данный момент, или что-то подобное, чтобы можно было сопоставить все журналы для этого запроса.

Другим хорошим примером является System.Random в .NET. Довольно часто известно, что вы не должны создавать новый экземпляр каждый раз, когда вы хотите использовать Random , поэтому некоторые люди создают один экземпляр и помещают его в статическую переменную... но это неловко, потому что Random не защищен от потоков. Вместо этого требуется один экземпляр для каждого потока, который должен быть соответствующим образом затравлен. Локальный поток < T > отлично подходит для этого.

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

В общем, это случай нежелания передавать слишком много контекста по всему месту. Вы можете сделать так, чтобы каждый отдельный вызов метода включал в себя «случайный контекст» или «LogContext» - но он был бы в путь чистоты вашего API - и цепь была бы разорвана, если бы вам когда-либо пришлось позвонить в другой API, который бы перезвонил к вам через виртуальный метод или что-то подобное.

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

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

6
ответ дан 30 November 2019 в 13:05
поделиться

Вам не нужно создавать класс - просто напишите функцию:

#include <vector>
#include <algorithm>
using namespace std;

struct Person {
    int age;
    int getage() const {
        return age;
    }
};

bool cmp( const Person * a, const Person * b ) {
    return a->getage() < b->getage() ;
}

int main() {
    vector <Person*> v;
    sort( v.begin(), v.end(), cmp );
}
-121--1611659-

Подумайте об этом таким образом.

«Для обычных случаев взлома SQL, какую объектно-ориентированную вещь я должен был делать в первую очередь?»

Проблема не в том, что ORM сложен. Это то, что ваш мозг искривлен в пресс-форме SQL, что затрудняет четкое изображение объектов.

Общие правила:

  • Если вы думаете, что это простой ВЫБОР ИЗ МЕСТА, остановитесь. Спросите, какие объекты необходимо увидеть в результирующем наборе. Затем найдите эти объекты и работайте с менеджером объектов.

  • Если вы думаете, что это простое соединение, остановитесь. Спросите, какой основной объект вам нужен. Помните, что объекты не используют внешние ключи. Присоединиться ничего не значит. Похоже, что объект разрывает 1NF и содержит весь набор связанных с ним объектов. Затем найдите «первичные» объекты и работайте с менеджером объектов. Используйте запросы связанных объектов для поиска связанных объектов.

  • Если вы думаете, что это ВНЕШНЕЕ СОЕДИНЕНИЕ, остановитесь. Спросите, какие две вещи вы хотите увидеть в результирующем наборе. Внешнее соединение - это вещи, которые присоединятся к UNIONED с вещами, которые не присоединяются. Какие вещи в первую очередь. Затем найдите «первичные» объекты и работайте с менеджером объектов. Некоторые будут иметь наборы связанных объектов. Некоторые нет.

  • Если вы думаете, что это WHERE EXISTS или WHERE IN с подзапросом, ваша модель, вероятно, является неполной. Иногда это требует модного объединения. Но если вы делаете такие проверки, это обычно означает, что вам нужна собственность в вашей модели.

  • Если вы считаете, что вам нужен ВЫБОР, вы пропустили лодку полностью. Это всего лишь набор Python. Вы просто получаете значения столбцов в набор Python. Это различные значения.

  • Если вы считаете, что вам нужна GROUP BY, вы игнорируете Python collections.defaultdict . Использование Python to в GROUP BY обычно быстрее, чем смеяться с SQL.

    За исключением хранилища данных. Чего не стоит делать в Джанго. Для хранения данных необходимо использовать SQLAlchemy.

-121--3279183-

Это помогает передавать значение вниз по стеку. Это удобно, когда вам нужно значение вниз по стеку вызовов, но нет пути (или преимущества) передать это значение в место, где оно необходимо в качестве параметра для метода. Приведенный выше пример сохранения текущего StartRequest в ThreaLocal является хорошим примером того, что альтернативой является передача этого параметра в стеке туда, где он будет необходим.

4
ответ дан 30 November 2019 в 13:05
поделиться

В Java локальное хранилище потоков может быть полезно в веб-приложении, где один запрос обычно обрабатывается данным потоком. Возьмем, к примеру, Spring Security, фильтр безопасности выполнит аутентификацию, а затем сохранит учетные данные пользователей в локальной переменной Thread.

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

3
ответ дан 30 November 2019 в 13:05
поделиться
1
ответ дан 30 November 2019 в 13:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: