Почему вы должны блокировать потоки?

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

Обеспечивает ли блокировка то, чтобы потоки НЕ выполнялись одновременно?

Кроме того, что неправильно с потоками, выполняемыми одновременно? Разве это еще не лучше? (более быстрое общее выполнение)

Когда вы блокируете потоки, они будут блокировать их все или вы можете выбрать, какие из них вы хотите заблокировать? (Независимо от того, какая блокировка действительно …)

Я имею в виду использование функций блокировки, таких как lock () и получение в потоковом модуле btw …

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

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

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

    Или, может быть … и это произошло в компании, в которой я работаю … у вас такие ошибки, но не знаю, что они там, потому что вряд ли у ваших клиентов будет более 4 процессоров. Затем все они начинают покупать 16-процессорные коробки … и ваше программное обеспечение работает столько же потоков, сколько и ядра процессора, поэтому теперь в 4 раза больше потоков, и вы внезапно рушитесь или получаете неправильные результаты.

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

    Если потоки записываются в разные файлы, эта проблема никогда не возникает. Итак, это одно из решений: ваши потоки пишут в разные файлы и при необходимости объедините их. Но это не всегда возможно; иногда есть только одно.

    Это не должны быть файлы. Предположим, вы пытаетесь просто подсчитать количество вхождений буквы «А» в кучу разных файлов, по одному потоку на файл. Вы думаете, ну, очевидно, я просто хочу, чтобы все потоки увеличивали одинаковое расположение памяти каждый раз, когда они видят «A.» Но! Когда вы начинаете увеличивать переменную, хранящую счет, компьютер считывает переменную в регистр, увеличивает регистр и затем сохраняет значение обратно. Что делать, если два потока одновременно считывают значение, увеличивают его одновременно и сохраняют в одно и то же время? Они начинаются с, скажем, 10, увеличивают его до 11, сохраняют 11 назад. Таким образом, счетчик 11, когда ему должно быть 12: вы потеряли один счет.

    Приобретение замков может быть дорогостоящим, так как вам нужно подождать, пока кто-то еще не воспользуется ресурсом. Вот почему Python Global Interpreter Lock является узким местом производительности. Поэтому вы можете вообще отказаться от использования общих ресурсов. Вместо того, чтобы использовать одну ячейку памяти для хранения числа «A» в ваших файлах, каждый поток сохраняет свой собственный счет, и вы добавляете их все в конец (аналогично решению, которое я предложил с файлами, достаточно забавно) ,

    Во-первых, блокировки предназначены для защиты ресурсов; потоки не «заблокированы» или «разблокированы» они / приобретают / блокируют (на ресурсе) и / освобождают / блокируют (на ресурсе).

    Вы правы, что хотите, чтобы потоки выполнялись одновременно, насколько это возможно, но давайте посмотрим на это:

     y=10 def doStuff( x ): global y a = 2 * y b = y / 5 y = a + b + x print y t1 = threading.Thread( target=doStuff, args=(8,) ) t2 = threading.Thread( target=doStuff, args=(8,) ) t1.start() t2.start() t1.join() t2.join() 

    Теперь вы можете знать, что либо один из этих потоков может быть завершен и напечатан первым. Вы ожидаете увидеть оба выхода 30.

    Но они не могут.

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

    Подумайте об этом так:

    t1 счастливо выполняет код, и он попадает

     a = 2 * y 

    Теперь t1 имеет a = 20 и перестает выполняться некоторое время. t2 становится активным, а t1 ожидает большего времени процессора. t2 выполняет:

     a = 2 * y b = y / 5 y = a + b + x 

    в этот момент глобальная переменная y = 30

    t2 останавливается на бит, а t1 снова поднимается. он выполняет:

     b = y / 5 y = a + b + x 

    Поскольку y равно 30, когда b задано, b = 6 и y теперь установлено на 34.

    порядок отпечатков также не является детерминированным, и вы можете сначала получить первые 30 или 34.

    используя замок, который у нас был бы:

     global l l = threading.Lock() def doStuff( x ): global y global l l.acquire() a = 2 * y b = y / 5 y = a + b + x print y l.release() 

    Это обязательно делает этот раздел кода линейным – только по одному потоку за раз. Но если ваша целая программа является последовательной, вы все равно не должны использовать потоки. Идея заключается в том, что вы получаете ускорение на основе процента кода, который у вас есть, который может выполнять внешние блокировки и запускаться параллельно. Это (одна из причин), почему использование потоков в двухъядерной системе не удваивает производительность для всего.

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

    Надеюсь, этого достаточно, чтобы продолжить!