Шаг 17.
Библиотека PyQt5. Знакомство с PyQt5. Многопоточные приложения. Модуль queue: создание очереди заданий
На этом шаге мы перечислим классы, используемые для создания очереди, и их основные методы.
На предыдущем шаге мы рассмотрели возможность обмена сигналами между потоками. Теперь предположим, что запущены десять потоков, которые ожидают задания в бесконечном цикле.
Как передать задание одному потоку, а не всем сразу? И как определить, какому потоку передать задание? Можно, конечно, создать список в глобальном пространстве имен и добавлять задания в этот
список, но в этом случае придется решать вопрос о совместном использовании одного ресурса сразу десятью потоками. Ведь если потоки будут получать задания одновременно, то одно задание
могут получить сразу несколько потоков, и какому-либо потоку не хватит заданий, - возникнет исключительная ситуация. Попросту говоря, возникает ситуация, когда вы пытаетесь сесть на стул,
а другой человек одновременно пытается вытащить его из-под вас. Думаете, что успеете сесть?
Модуль queue, входящий в состав стандартной библиотеки Python, позволяет решить эту проблему. Модуль содержит несколько классов, которые реализуют разного рода
потоко-безопасные очереди. Перечислим эти классы:
- Queue - очередь (первым пришел, первым вышел). Формат конструктора:
<Oбъект> = Queue([maxsize=0])
Пример:
>>> import queue
>>> q = queue.Queue()
>>> q.put_nowait ("elem1")
>>> q.put_nowait ("elem2")
>>> q.get_nowait()
'elem1'
>>> q.get_nowait()
'elem2'
- LifoQueue - стек (последним пришел, первым вышел). Формат конструктора:
<Объект> = LifoQueue([maxsize=0])
Пример:
>>> q = queue.LifoQueue()
>>> q.put_nowait ("elem1")
>>> q.put_nowait ("elem2")
>>> q.get_nowait()
'elem2'
>>> q.get_nowait()
'elem1'
- PriorityQueue - очередь с приоритетами. Элементы очереди должны быть кортежами, в которых первым элементом является число, означающее приоритет, а вторым -
значение элемента. При получении значения возвращается элемент с наивысшим приоритетом (наименьшим значением в первом параметре кортежа). Формат конструктора класса:
<Объект> = PriorityQueue([maxsize=0])
Пример:
>>> q = queue.PriorityQueue ()
>>> q.put_nowait ( (10, "elem1") )
>>> q.put_nowait ( (3, "elem2") )
>>> q.put_nowait ( (12, "elem3") )
>>> q.get_nowait()
(3, 'elem2')
>>> q.get_nowait()
(10, 'elem1')
>>> q.get_nowait()
(12, 'elem3')
Параметр maxsize во всех трех случаях задает максимальное количество элементов, которое может содержать очередь. Если параметр равен нулю (значение по умолчанию) или отрицательному значению, то размер очереди не ограничен.
Экземпляры этих классов поддерживают следующие методы;
- put (<Элемент>[, block=True] [, timeout=None]) - добавляет элемент в очередь. Если в параметре block указано значение True, поток будет ожидать
возможности добавления элемента, - при этом в параметре timeout можно указать максимальное время ожидания в секундах. Если элемент не удалось добавить, то возбуждается исключение
queue.Full. В случае передачи параметром block значения False очередь не будет ожидать, когда появится возможность добавить в нее новый элемент, и в случае невозможности
сделать это возбудит исключение queue.Full немедленно;
- put_nowait (<Элемент>) - добавление элемента без ожидания. Эквивалентно: put(<Элемент>, False);
- get ([block=True] [, timeout=None]) - возвращает элемент, при этом удаляя его из очереди. Если в параметре block указано значение True, то поток будет ожидать возможности
извлечения элемента, - при этом в параметре timeout можно указать максимальное время ожидания в секундах. Если элемент не удалось получить, то возбуждается исключение queue.Empty.
В случае передачи параметром block значения False очередь не будет ожидать, когда появится возможность извлечь из нее элемент, и в случае невозможности сделать это возбудит
исключение queue.Empty немедленно;
- get_nowait () - извлечение элемента без ожидания. Эквивалентно вызову get(False);
- join() - блокирует поток, пока не будут обработаны все задания в очереди. Другие потоки после обработки текущего задания должны вызывать метод task_done(). Как
только все задания будут обработаны, поток будет разблокирован;
- task_done () - этот метод должны вызывать потоки после обработки задания;
- qsize() - возвращает приблизительное количество элементов в очереди. Так как доступ к очереди имеют сразу несколько потоков, доверять этому значению не следует -
в любой момент времени количество элементов может измениться;
- empty () - возвращает True, если очередь пуста, и False - в противном случае;
- full () - возвращает True, если очередь содержит элементы, и False - в противном случае.
На следующем шаге мы закончим изучение этого вопроса.
Предыдущий шаг
Содержание
Следующий шаг