Я не эксперт Django, таким образом, этот ответ обычно о хранилищах сессии. Downvote, если я неправ.
Выбор хранилища сессии имеет эффект на производительность и масштабируемость. Это должно только быть большой проблемой, если у Вас есть очень распространенное приложение.
И база данных и хранилища сессии файловой системы (обычно) поддерживаются дисками, таким образом, у Вас может быть много сессий дешево (потому что диски являются дешевыми), но запросы должны будут часто ожидать данных, которые будут считаны (потому что диски являются медленными). RAM использования сессий Memcached, так будет стоить больше для поддержки того же количества параллельных сессий (потому что RAM является дорогой), но может быть быстрее (потому что RAM быстра).
сессии Файловой системы связываются с полем, куда Ваше приложение работает, таким образом, Вы не можете загрузить баланс между несколькими серверами приложений, если Ваш сайт становится огромным. База данных и memcached сессии позволяют Вам иметь несколько серверов приложений, говорящих с хранилищем сеанса совместной работы.
Выбор хранилища сессии также повлияет, как легкий это должно развернуть Ваш сайт. Изменение далеко от значения по умолчанию будет стоить некоторой сложности. Memcached и RDBMSs оба имеют их собственные сложности, но Ваше приложение, вероятно, будет использованием RDBMS так или иначе.
, Если у Вас нет очень распространенного приложения, простота должна быть большим беспокойством.
Другой подход должен хранить данные сессии в cookie (все это, не только идентификатор). Это имеет преимущество, которое хранилище сессии автоматически масштабирует с числом пользователей, но оно имеет недостатки также. (Или Ваша платформа) необходимо стараться остановить пользовательские данные сессии ковки. Также необходимо сохранить каждую сессию небольшой, потому что все это будет отправлено с каждым запросом.
В многопоточной среде вы должны позаботиться о синхронизация , чтобы два потока не нарушали состояние, одновременно выполняя модификации. В противном случае вы можете иметь условия гонки в вашем коде (например, см. печально известную аварию Therac-25 .) Вы также должны запланировать потоки для выполнения различных задач. Затем вы должны убедиться, что ваша синхронизация и планирование не вызывают тупиковой ситуации , когда несколько потоков будут ждать друг друга бесконечно.
Синхронизация
Для такой простой операции, как увеличение счетчика, требуется синхронизация:
counter += 1;
Предположим следующую последовательность событий:
счетчик
инициализируется на 0 счетчик
из памяти в процессор (0) счетчик
из память в процессор (0) счетчик
на процессоре счетчик
из процессора в память (1)
на процессоре счетчик
из процессора в память (1) В этот момент счетчик
равен 1, но оба потока действительно пытались его увеличить. Доступ к счетчику должен быть синхронизирован с помощью какого-то механизма блокировки:
lock (myLock) {
counter += 1;
}
Только одному потоку разрешено выполнять код внутри заблокированного блока. Два потока, выполняющие этот код, могут привести к такой последовательности событий:
Планирование
Планирование - это еще одна форма синхронизации, и вы должны использовать механизмы синхронизации потоков, такие как события, семафоры, передача сообщений и т. Д. Для запуска и остановки потоков. Вот упрощенный пример на C #:
AutoResetEvent taskEvent = new AutoResetEvent(false);
Task task;
// Called by the main thread.
public void StartTask(Task task) {
this.task = task;
// Signal the worker thread to perform the task.
this.taskEvent.Set();
// Return and let the task execute on another thread.
}
// Called by the worker thread.
void ThreadProc() {
while (true) {
// Wait for the event to become signaled.
this.taskEvent.WaitOne();
// Perform the task.
}
}
Вы заметите, что доступ к this.task
, вероятно, неправильно синхронизирован, что рабочий поток не может вернуть результаты обратно в основной поток, и что нет никакого способа сигнализировать рабочему потоку о завершении. Все это можно исправить на более сложном примере.
Тупик
Типичный пример тупика - это когда у вас есть две блокировки, и вы не заботитесь о том, как их получить. В какой-то момент вы приобретаете lock1
перед lock2
:
public void f() {
lock (lock1) {
lock (lock2) {
// Do something
}
}
}
В другой момент вы получаете lock2
перед lock1
:
public void g() {
lock (lock2) {
lock (lock1) {
// Do something else
}
}
}
Let '
Хотя ваши первоначальные различия в суммах чисел, как указали несколько респондентов, вероятно, являются результатом отсутствия синхронизации, если вы углубитесь в тему, имейте в виду, что в Как правило, вы не сможете точно воспроизвести числовые результаты, полученные в последовательной программе, с результатами параллельной версии той же программы. Арифметика с плавающей точкой не является строго коммутативной, ассоциативной или распределительной; черт возьми, это даже не закрыто.
И я прошу не согласиться с тем, что, я думаю, является здесь мнением большинства. Если вы пишете многопоточные программы для настольного компьютера с одним или несколькими многоядерными процессорами, то вы работаете на компьютере с общей памятью и должны заниматься программированием с общей памятью. В Java есть все возможности для этого.
В .Net одна вещь, которая удивила меня, когда я начал пытаться перейти в многопоточность, - это то, что вы не можете напрямую обновить элементы управления пользовательского интерфейса из любого потока, кроме потока, в котором элементы управления пользовательского интерфейса были созданы. on.
Есть способ обойти это, который заключается в использовании метода Control.Invoke для обновления элемента управления в другом потоке, но с первого раза это не очевидно на 100%!
Важная вещь, о которой нужно позаботиться (с несколькими ядрами и процессорами), - это согласованность кэша .
Самое важное, что нужно запомнить: вам действительно нужна многопоточность?
НЕ использовать глобальные переменные
НЕ использовать много блокировок (в лучшем случае вообще не использовать - хотя практически невозможно)
НЕ пытаются быть героем, реализуя сложные сложные протоколы MT
DO используют простые парадигмы. Т.е. разделите обработку массива на n кусочков одинакового размера - где n должно быть равно количеству процессоров
DO протестировать ваш код на разных машинах (используя один, два, много процессоров)
DO использует атомарные операции (такие как InterlockedIncrement ()
и т.п.)
- Какие известные проблемы с потоками? -
- Какую осторожность следует соблюдать при использовании потоков? -
Использование многопоточности на однопроцессорной машине для обработки нескольких задач, где каждая задача занимает примерно одинаковое время, не всегда очень эффективно. Например, вы можете решить создать десять потоков в своей программе, чтобы обработать десять отдельных задач. Если каждая задача занимает около 1 минуты, и вы используете десять потоков для выполнения этой обработки, у вас не будет доступа ни к одному из результатов задачи в течение всех 10 минут. Если вместо этого вы обрабатываете те же задачи, используя только один поток, вы увидите первый результат через 1 минуту, следующий результат через 1 минуту и так далее. thread может быть лучшим способом реализации программы.
Если вы запускаете большое количество потоков в процессе, накладные расходы на обслуживание потоков и переключение контекста могут стать значительными. Процессор будет тратить значительное время на переключение между потоками, и многие из потоков не смогут работать. Кроме того, один процесс с большим количеством потоков означает, что потоки в других процессах будут планироваться реже и не получат разумной доли процессорного времени.
Если несколько потоков должны совместно использовать многие из одних и тех же ресурсов, вы вряд ли увидите преимущества в производительности от многопоточности вашего приложения. Многие разработчики рассматривают многопоточность как своего рода волшебную палочку, которая дает автоматическое повышение производительности. К сожалению, многопоточность - это не волшебная палочка, как это иногда воспринимается. Если вы используете многопоточность из соображений производительности, вам следует очень внимательно измерить производительность вашего приложения в нескольких различных ситуациях, а не просто полагаться на какую-то несуществующую магию.
Координация доступа потоков к общим данным может быть большой производительностью убийца. Достичь хорошей производительности с несколькими потоками непросто при использовании плана грубой блокировки, потому что это приводит к низкому параллелизму и потокам, ожидающим доступа. В качестве альтернативы, детализированная стратегия блокировки увеличивает сложность и может также снизить производительность, если вы не выполните какую-то сложную настройку.
Использование нескольких потоков для эксплуатации машины с несколькими процессорами звучит как хорошая идея в теории, но на практике вам нужно быть осторожным. Чтобы получить какие-либо значительные преимущества в производительности, вам, возможно, придется разобраться с балансировкой потоков.
- Приведите примеры. -
Например, представьте приложение, которое получает входящую информацию о ценах от сеть, агрегирует и сортирует эту информацию, а затем отображает результаты на экране для конечного пользователя.
Для двухъядерной машины имеет смысл разделить задачу, скажем, на три потока. Первый поток занимается хранением входящей информации о ценах, второй поток обрабатывает цены, а последний поток обрабатывает отображение результатов.
После реализации этого решения предположим, что вы обнаружите, что обработка цены - это, безусловно, самый длинный этап, поэтому вы решили переписать код этого потока, чтобы повысить его производительность в три раза. К сожалению, это улучшение производительности в одном потоке может не отразиться на всем приложении. Это связано с тем, что два других потока могут не успевать за улучшенным потоком. Если поток пользовательского интерфейса не может справиться с более быстрым потоком обрабатываемой информации,
Я не могу привести вам примеров, кроме как указать вам на Google. Найдите основы работы с потоками, синхронизацию потоков, и вы получите больше совпадений, чем вы думаете.
Основная проблема потоковой передачи состоит в том, что потоки не знают друг о друге, поэтому они с радостью наступают друг другу на ноги, как 2 человека. пытаясь пройти через 1 дверь, иногда они будут проходить один за другим, но иногда они оба будут пытаться пройти одновременно и застрянут. Это трудно воспроизвести, трудно отладить и иногда вызывает проблемы. Если у вас есть потоки и вы видите «случайные» сбои, это, вероятно, проблема.
Поэтому следует проявлять осторожность с общими ресурсами. Если вы и ваш друг хотите кофе, но у вас всего одна ложка, вы не можете использовать ее одновременно, одному из вас придется ждать другого. Для «синхронизации» доступа к общей ложке используется метод блокировки. Убедитесь, что вы заблокировали общий ресурс, прежде чем использовать его, и отпустите его после этого. Если у кого-то есть блокировка, вы ждете, пока он ее не снимет.
Следующая проблема связана с этими блокировками, иногда у вас может быть программа, которая настолько сложна, что вы получаете блокировку, делаете что-то еще, затем получаете доступ к другому ресурсу и попробуйте получить блокировку для этого - но у какого-то другого потока есть этот 2-й ресурс, поэтому вы сидите и ждете ... но если этот 2-й поток ожидает блокировки, которую вы удерживаете для 1-го ресурса ... он будет сидеть и ждать. И ваше приложение просто сидит там. Это называется взаимоблокировкой, когда два потока ждут друг друга.
Эти 2 проблемы представляют собой подавляющее большинство проблем с потоками.
Я бы сделал очень вопиющее заявление:
НЕ используйте разделяемую память.
ОБЯЗАТЕЛЬНО используйте передачу сообщений.
В качестве общего совета попробуйте ограничить количество разделяемое состояние и предпочитают архитектуры, управляемые событиями.
Не обманывайтесь, думая, что вы понимаете трудности параллелизма, пока не разберетесь в реальном проекте.
Все примеры взаимоблокировок, живых блокировок, синхронизации и т. Д. кажутся простыми, и они есть. Но они введут вас в заблуждение, потому что «сложность» реализации параллелизма, о которой все говорят, возникает тогда, когда она используется в реальном проекте, когда вы не контролируете все.
ОБЯЗАТЕЛЬНО подумайте о том, как вы будете тестировать свой код, и выделите для этого достаточно времени. Юнит-тесты усложняются. Возможно, вы не сможете вручную протестировать свой код - по крайней мере, ненадежно.
ОБЯЗАТЕЛЬНО подумайте о времени жизни потока и о том, как потоки будут завершены. Не убивайте темы. Обеспечьте механизм, чтобы они выходили изящно.
НЕОБХОДИМО добавить в код какой-нибудь журнал отладки, чтобы вы могли видеть, что ваши потоки ведут себя правильно как при разработке, так и при производстве, когда что-то выходит из строя.
ОБЯЗАТЕЛЬНО используйте хорошую библиотеку для обработки потоков, а не накатываете собственное решение (если можете). Например, java.util.concurrency
НЕ предполагайте, что общий ресурс является потокобезопасным.
НЕ ДЕЛАЙТЕ ЭТОГО. Например, используйте контейнер приложения, который может позаботиться о проблемах с потоками. Используйте сообщения.
Я удивлен, что еще никто не указал на столбцы Эффективный параллелизм Херба Саттера. На мой взгляд, это необходимо прочитать, если вы хотите приблизиться к темам.
a) Всегда делайте только 1 поток ответственным за время жизни ресурса. Таким образом, поток A не удалит ресурс, который нужен потоку B - если B владеет ресурсом
b) Ожидайте неожиданного
Я применяю свой новый нашел знания о многопоточности везде
[Emphasis added]
DO remember that a little knowledge is dangerous. Knowing the threading API of your platform is the easy bit. Knowing why and when you need to use synchronisation is the hard part. Reading up on "deadlocks", "race-conditions", "priority inversion" will start you in understanding why.
The details of when to use synchronisation are both simple (shared data needs synchronisation) and complex (atomic data types used in the right way don't need synchronisation, which data is really shared): a lifetime of learning and very solution specific.
Don't start new threads unless you really need to. Starting threads is not cheap and for short running tasks starting the thread may actually take more time than executing the task itself. If you're on .NET take a look at the built in thread pool, which is useful in a lot of (but not all) cases. By reusing the threads the cost of starting threads is reduced.
EDIT: A few notes on creating threads vs. using thread pool (.NET specific)
Generally try to use the thread pool. Exceptions:
There are probably more exceptions and I am not claiming that this is the definitive answer. It is just what I could think of atm.
I notice you are writing in java and that nobody else mentioned books so Java Concurrency In Practice should be your multi-threaded bible.
Я согласен практически со всеми ответами на данный момент.
Хорошая стратегия кодирования - минимизировать или исключить количество данных, которые совместно используются между потоками, насколько это возможно. Вы можете сделать это следующим образом:
Старайтесь не допускать, чтобы несколько потоков конкурировали за один и тот же «ресурс» ввода-вывода, будь то файл на диске или таблица базы данных, вызов веб-службы или что-то еще. Это вызовет конкуренцию, поскольку несколько потоков борются за один и тот же ресурс.
Вот чрезвычайно надуманный пример OTT. В реальном приложении вы должны ограничить количество потоков, чтобы уменьшить накладные расходы на планирование:
Вместо того, чтобы гадать, как разделить задачи, профилируйте свое приложение и выделите те биты, которые (а) очень медленные, и (б) могут выполняться асинхронно. Это хорошие кандидаты для отдельного потока.
И вот чего следует избегать:
Есть два вида людей, которые не используют многопоточность.
1) Те, кто не понимает концепции и не знает, как ее запрограммировать. 2) Те, кто полностью понимает концепцию и знает, как сложно понять ее правильно.