Что такое DOS многопоточности и DONTs? [закрытый]

Я не эксперт Django, таким образом, этот ответ обычно о хранилищах сессии. Downvote, если я неправ.

Производительность и Масштабируемость

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

И база данных и хранилища сессии файловой системы (обычно) поддерживаются дисками, таким образом, у Вас может быть много сессий дешево (потому что диски являются дешевыми), но запросы должны будут часто ожидать данных, которые будут считаны (потому что диски являются медленными). RAM использования сессий Memcached, так будет стоить больше для поддержки того же количества параллельных сессий (потому что RAM является дорогой), но может быть быстрее (потому что RAM быстра).

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

Простота

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

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

Премия

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

28
задан casperOne 30 August 2012 в 19:03
поделиться

18 ответов

В многопоточной среде вы должны позаботиться о синхронизация , чтобы два потока не нарушали состояние, одновременно выполняя модификации. В противном случае вы можете иметь условия гонки в вашем коде (например, см. печально известную аварию Therac-25 .) Вы также должны запланировать потоки для выполнения различных задач. Затем вы должны убедиться, что ваша синхронизация и планирование не вызывают тупиковой ситуации , когда несколько потоков будут ждать друг друга бесконечно.

Синхронизация

Для такой простой операции, как увеличение счетчика, требуется синхронизация:

counter += 1;

Предположим следующую последовательность событий:

  • счетчик инициализируется на 0
  • поток A извлекает счетчик из памяти в процессор (0)
  • переключатель контекста
  • поток B извлекает счетчик из память в процессор (0)
  • поток B увеличивает счетчик на процессоре
  • поток B записывает обратно счетчик из процессора в память (1)
  • переключатель контекста
  • поток A увеличивает счетчик на процессоре
  • поток A записывает обратно счетчик из процессора в память (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 '

28
ответ дан 28 November 2019 в 02:22
поделиться

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

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

0
ответ дан 28 November 2019 в 02:22
поделиться

В .Net одна вещь, которая удивила меня, когда я начал пытаться перейти в многопоточность, - это то, что вы не можете напрямую обновить элементы управления пользовательского интерфейса из любого потока, кроме потока, в котором элементы управления пользовательского интерфейса были созданы. on.

Есть способ обойти это, который заключается в использовании метода Control.Invoke для обновления элемента управления в другом потоке, но с первого раза это не очевидно на 100%!

0
ответ дан 28 November 2019 в 02:22
поделиться

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

1
ответ дан 28 November 2019 в 02:22
поделиться

YAGNI

Самое важное, что нужно запомнить: вам действительно нужна многопоточность?

3
ответ дан 28 November 2019 в 02:22
поделиться

НЕ использовать глобальные переменные

НЕ использовать много блокировок (в лучшем случае вообще не использовать - хотя практически невозможно)

НЕ пытаются быть героем, реализуя сложные сложные протоколы MT

DO используют простые парадигмы. Т.е. разделите обработку массива на n кусочков одинакового размера - где n должно быть равно количеству процессоров

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

DO использует атомарные операции (такие как InterlockedIncrement () и т.п.)

5
ответ дан 28 November 2019 в 02:22
поделиться

- Какие известные проблемы с потоками? -

- Какую осторожность следует соблюдать при использовании потоков? -

Использование многопоточности на однопроцессорной машине для обработки нескольких задач, где каждая задача занимает примерно одинаковое время, не всегда очень эффективно. Например, вы можете решить создать десять потоков в своей программе, чтобы обработать десять отдельных задач. Если каждая задача занимает около 1 минуты, и вы используете десять потоков для выполнения этой обработки, у вас не будет доступа ни к одному из результатов задачи в течение всех 10 минут. Если вместо этого вы обрабатываете те же задачи, используя только один поток, вы увидите первый результат через 1 минуту, следующий результат через 1 минуту и ​​так далее. thread может быть лучшим способом реализации программы.

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

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

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

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

- Приведите примеры. -

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

Для двухъядерной машины имеет смысл разделить задачу, скажем, на три потока. Первый поток занимается хранением входящей информации о ценах, второй поток обрабатывает цены, а последний поток обрабатывает отображение результатов.

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

5
ответ дан 28 November 2019 в 02:22
поделиться

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

Основная проблема потоковой передачи состоит в том, что потоки не знают друг о друге, поэтому они с радостью наступают друг другу на ноги, как 2 человека. пытаясь пройти через 1 дверь, иногда они будут проходить один за другим, но иногда они оба будут пытаться пройти одновременно и застрянут. Это трудно воспроизвести, трудно отладить и иногда вызывает проблемы. Если у вас есть потоки и вы видите «случайные» сбои, это, вероятно, проблема.

Поэтому следует проявлять осторожность с общими ресурсами. Если вы и ваш друг хотите кофе, но у вас всего одна ложка, вы не можете использовать ее одновременно, одному из вас придется ждать другого. Для «синхронизации» доступа к общей ложке используется метод блокировки. Убедитесь, что вы заблокировали общий ресурс, прежде чем использовать его, и отпустите его после этого. Если у кого-то есть блокировка, вы ждете, пока он ее не снимет.

Следующая проблема связана с этими блокировками, иногда у вас может быть программа, которая настолько сложна, что вы получаете блокировку, делаете что-то еще, затем получаете доступ к другому ресурсу и попробуйте получить блокировку для этого - но у какого-то другого потока есть этот 2-й ресурс, поэтому вы сидите и ждете ... но если этот 2-й поток ожидает блокировки, которую вы удерживаете для 1-го ресурса ... он будет сидеть и ждать. И ваше приложение просто сидит там. Это называется взаимоблокировкой, когда два потока ждут друг друга.

Эти 2 проблемы представляют собой подавляющее большинство проблем с потоками.

9
ответ дан 28 November 2019 в 02:22
поделиться

Я бы сделал очень вопиющее заявление:

НЕ используйте разделяемую память.

ОБЯЗАТЕЛЬНО используйте передачу сообщений.

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

13
ответ дан 28 November 2019 в 02:22
поделиться

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

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

0
ответ дан 28 November 2019 в 02:22
поделиться

ОБЯЗАТЕЛЬНО подумайте о том, как вы будете тестировать свой код, и выделите для этого достаточно времени. Юнит-тесты усложняются. Возможно, вы не сможете вручную протестировать свой код - по крайней мере, ненадежно.

ОБЯЗАТЕЛЬНО подумайте о времени жизни потока и о том, как потоки будут завершены. Не убивайте темы. Обеспечьте механизм, чтобы они выходили изящно.

НЕОБХОДИМО добавить в код какой-нибудь журнал отладки, чтобы вы могли видеть, что ваши потоки ведут себя правильно как при разработке, так и при производстве, когда что-то выходит из строя.

ОБЯЗАТЕЛЬНО используйте хорошую библиотеку для обработки потоков, а не накатываете собственное решение (если можете). Например, java.util.concurrency

НЕ предполагайте, что общий ресурс является потокобезопасным.

НЕ ДЕЛАЙТЕ ЭТОГО. Например, используйте контейнер приложения, который может позаботиться о проблемах с потоками. Используйте сообщения.

1
ответ дан 28 November 2019 в 02:22
поделиться

Я удивлен, что еще никто не указал на столбцы Эффективный параллелизм Херба Саттера. На мой взгляд, это необходимо прочитать, если вы хотите приблизиться к темам.

1
ответ дан 28 November 2019 в 02:22
поделиться

a) Всегда делайте только 1 поток ответственным за время жизни ресурса. Таким образом, поток A не удалит ресурс, который нужен потоку B - если B владеет ресурсом

b) Ожидайте неожиданного

1
ответ дан 28 November 2019 в 02:22
поделиться

Я применяю свой новый нашел знания о многопоточности везде

[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.

1
ответ дан 28 November 2019 в 02:22
поделиться

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:

  • Long running CPU bound tasks and blocking tasks are not ideal run on the thread pool cause they will force the pool to create additional threads.
  • All thread pool threads are background threads, so if you need your thread to be foreground, you have to start it yourself.
  • If you need a thread with different priority.
  • If your thread needs more (or less) than the standard 1 MB stack space.
  • If you need to be able to control the life time of the thread.
  • If you need different behavior for creating threads than that offered by the thread pool (e.g. the pool will throttle creating of new threads, which may or may not be what you want).

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.

2
ответ дан 28 November 2019 в 02:22
поделиться

I notice you are writing in java and that nobody else mentioned books so Java Concurrency In Practice should be your multi-threaded bible.

7
ответ дан 28 November 2019 в 02:22
поделиться

Я согласен практически со всеми ответами на данный момент.

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

  • Используя статические переменные потока (хотя не переусердствуйте, это потребляет больше памяти на каждый поток, в зависимости от вашей операционной системы).
  • Упаковка всех состояний, используемых каждым нить в класс, затем гарантируется, что каждый поток получит себе ровно один экземпляр класса состояния. Думайте об этом как о «свертывании собственного потока-статики», но с большим контролем над процессом.
  • Маршалинг данных по значению между потоками вместо совместного использования одних и тех же данных. Либо сделайте классы передачи данных неизменными, либо гарантируйте, что все вызовы между потоками будут синхронными, либо и то, и другое.

Старайтесь не допускать, чтобы несколько потоков конкурировали за один и тот же «ресурс» ввода-вывода, будь то файл на диске или таблица базы данных, вызов веб-службы или что-то еще. Это вызовет конкуренцию, поскольку несколько потоков борются за один и тот же ресурс.

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

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

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

И вот чего следует избегать:

  • Кальки, обращения к базе данных, вызовы служб и т. Д. - все в одном потоке, но запускаются несколько раз «для повышения производительности».
]
3
ответ дан 28 November 2019 в 02:22
поделиться

Есть два вида людей, которые не используют многопоточность.

1) Те, кто не понимает концепции и не знает, как ее запрограммировать. 2) Те, кто полностью понимает концепцию и знает, как сложно понять ее правильно.

16
ответ дан 28 November 2019 в 02:22
поделиться
Другие вопросы по тегам:

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