Thread.sleep метод
Содержание:
- 2. Режим гибернации
- Метод after() — Погружение в сон для Tkinter
- Overloads
- Sleep(Int32)
- Sleep(TimeSpan)
- Вызов sleep() с декораторами
- Файл — это очень уж странный предмет…
- Вызов sleep() в Tkinter и wxPython
- Sleep Until a TimePoint
- SIGTERM vs SIGKILL: Почему вы должны использовать SIGTERM вместо SIGKILL?
- Использование Queues
- BUGS top
2. Режим гибернации
Режим гибернации (Hibernate, Suspend to disk) деактивирован в Ubuntu по умолчанию и предусматривает отключение питания компьютера с переносом данных из оперативной памяти в раздел подкачки. Разумеется, для корректной работы данного режима размер раздела подкачки должен превышать объём доступной оперативной памяти. Кроме того, данный режим нередко работает некорректно из-за проблем с прошивками материнских плат. Ещё один его недостаток — затраты времени на запись и чтение данных. Для активации режима гибернации необходимо добавить параметр ядра ОС и создать файл конфигурации Polkit.
Начнём с параметра ядра ОС. Он предназначен для передачи имени файла устройства раздела подкачки, в котором будут сохраняться данные состояния ОС. Чтобы узнать имя этого файла, можно воспользоваться следующей командой:
Необходимое имя должно находиться в столбце NAME (Рисунок 5).
Рисунок 5. Список разделов подкачки системы
В случае его отсутствия у вас не активирован раздел подкачки.
Это имя следует добавить в строку параметров ядра ОС, передаваемую системным загрузчиком GRUB, в форме значения параметра resume. Для этого нужно открыть файл конфигурации /etc/default/grub и отредактировать строку GRUB_CMDLINE_LINUX_DEFAULT. Проще всего это сделать с помощью следующей команды:
Предположим, что именем файла устройства раздела подкачки является /dev/sda5. Тогда строку
придёётся заменить на строку
После этого нужно сохранить изменения в открытом файле (Рисунок 6).
Рисунок 6. Измененные параметры ядра Linux
В заключение нужно обновить конфигурацию системного загрузчика с помощью следующей команды:
Помимо этого следует создать файл конфигурации Polkit в директории /etc/polkit-1/localauthority/50-local.d/ с именем com.ubuntu.enable-hibernate.pkla и следующим содержимым:
Identity=unix-user:* Action=org.freedesktop.login1.hibernate;org.freedesktop.login1.handle-hibernate-key;org.freedesktop.login1;org.freedesktop.login1.hibernate-multiple-sessions;org.freedesktop.login1.hibernate-ignore-inhibit ResultActive=yes
Теперь приложение для изменения параметров системы позволит активировать режим гибернации при нажатии на кнопку включения (Рисунок 7).
Рисунок 7. Режим гибернации в меню приложения для изменения параметров системы
Это повод проверить спящий режим Ubuntu 18.04 с гибернацией. При отсутствии результата, вы можете снова деактивировать его, удалив созданный файл конфигурации с помощью команды:
Если же данный режим будет работать корректно, вы можете использовать кнопку включения для его активации. Если вы хотите активировать его при закрытии крышки ноутбука, вам придётся воспользоваться последовательностью специальных команд. Потому что ни приложение для изменения параметров системы, ни утилита GNOME Tweaks не позволёт сделать этого. Вот эти команды:
Первая команда устанавливает режим при питании компьютера от сети, вторая — от батареи. Для проверки корректности установки значений параметров конфигурации может использоваться специальная утилита с графическим интерфейсом. Она устанавливается с помощью следующей команды:
По окончании установки достаточно запустить её (имя в меню Редактор d-conf), осуществить переход org — gnome — settings-daemon — plugins — power и проверить значения параметров lid-close-ac-action и lid-close-battery-action (Рисунок 8).
Рисунок 8. Активированный режим гибернации при закрытии крышки ноутбука
Если вас не устраивает активируемый при закрытии крышки ноутбука режим гибернации, вы можете вернуть режим ожидания с помощью следующих команд:
Также может возникнуть вопрос: «А как активировать режим гибернации из системного меню?». Вообще, такой возможности не предусмотрено, но вы можете установить расширение GNOME Shell под названием «Hibernate Status Button». Установка расширения осуществляется с помощью Менеджера приложений Ubuntu (само расширение размещено в разделе Дополнения на вкладке Расширения GNOME Shell) (Рисунок 9).
Рисунок 9. Расширение «Hibernate Status Button» в списке расширений Менеджера приложений Ubuntu
После установки расширения в системном меню появится соответствующая кнопка (Рисунок 10).
Рисунок 10. Кнопка для перехода в режим гибернации
Если нажать на клавишу Alt при показе системного меню, кнопка перехода в режим гибернации будет заменена на кнопку перехода в гибридный режим сна.
Ну и о командах для самостоятельной активации данного режима. Это команда для перехода в режим гибернации с помощью утилиты systemctl:
А это вызов соответствующего метода DBus:
Метод after() — Погружение в сон для Tkinter
tkinter является частью стандартной библиотеки Python. В случае, если вы используете заранее установленную версию Python на Linux или Mac, он может быть вам недоступен. При получении ошибки стоит самостоятельно добавить его в систему. В том случае, если вы ранее установили Python сами, должен быть доступен.
Начнем с разбора примера, где используется . Запустите следующий код и посмотрите, что произойдет при неправильном добавлении вызова в Python:
Python
import tkinter
import time
class MyApp:
def __init__(self, parent):
self.root = parent
self.root.geometry(«400×400″)
self.frame = tkinter.Frame(parent)
self.frame.pack()
b = tkinter.Button(text=»click me», command=self.delayed)
b.pack()
def delayed(self):
time.sleep(3)
if __name__ == «__main__»:
root = tkinter.Tk()
app = MyApp(root)
root.mainloop()
1 15 |
importtkinter importtime classMyApp def__init__(self,parent) self.root=parent self.root.geometry(«400×400») self.frame=tkinter.Frame(parent) self.frame.pack() b=tkinter.Button(text=»click me»,command=self.delayed) b.pack() defdelayed(self) if__name__==»__main__» root=tkinter.Tk() app=MyApp(root) root.mainloop() |
После запуска кода нажмите кнопку в GUI. Кнопка не будет реагировать три секунды, ожидая завершения . Если в приложении есть другие кнопки, на них тоже нельзя будет нажать. Закрыть приложение во время сна нельзя, так как оно не будет откликаться на событие закрытия.
Для должного погружения в сон потребуется использовать :
Python
import tkinter
class MyApp:
def __init__(self, parent):
self.root = parent
self.root.geometry(«400×400»)
self.frame = tkinter.Frame(parent)
self.frame.pack()
self.root.after(3000, self.delayed)
def delayed(self):
print(‘Я задержался’)
if __name__ == «__main__»:
root = tkinter.Tk()
app = MyApp(root)
root.mainloop()
1 10 |
importtkinter classMyApp def__init__(self,parent) self.root=parent self.root.geometry(«400×400») self.frame=tkinter.Frame(parent) self.frame.pack() defdelayed(self) print(‘Я задержался’) if__name__==»__main__» root=tkinter.Tk() app=MyApp(root) root.mainloop() |
Здесь создается приложение, высота которого 400 пикселей, и ширина также 400 пикселей. На нем нет виджетов. Оно только показывает фрейм. Затем вызывается , где является отсылкой к объекту . принимает два аргумента:
- Количество миллисекунд для сна;
- Метод который вызовется после завершения сна.
В данном случае приложение выведет строку в стандартный поток вывода (stdout) через 3 секунды. Можно рассматривать как Tkinter-версию того же , только он добавляет способность вызова функции после завершения сна.
Данную функциональность можно использовать для улучшения работы пользователя. Добавив в Python вызов , можно ускорить процесс загрузки приложения, после чего начать какой-то длительный процесс. В таком случае пользователю не придется ждать открытия приложения.
Overloads
Suspends the current thread for the specified number of milliseconds. |
|
Suspends the current thread for the specified amount of time. |
Sleep(Int32)
Suspends the current thread for the specified number of milliseconds.
Parameters
-
millisecondsTimeout
- Int32
The number of milliseconds for which the thread is suspended. If the value of the argument is zero, the thread relinquishes the remainder of its time slice to any thread of equal priority that is ready to run. If there are no other threads of equal priority that are ready to run, execution of the current thread is not suspended.
Exceptions
ArgumentOutOfRangeException
The time-out value is negative and is not equal to Infinite.
Examples
The following example uses the Sleep method to block the application’s main thread.
Remarks
The thread will not be scheduled for execution by the operating system for the amount of time specified. This method changes the state of the thread to include .
You can specify Timeout.Infinite for the parameter to suspend the thread indefinitely. However, we recommend that you use other System.Threading classes such as Mutex, Monitor, EventWaitHandle, or Semaphore instead to synchronize threads or manage resources.
The system clock ticks at a specific rate called the clock resolution. The actual timeout might not be exactly the specified timeout, because the specified timeout will be adjusted to coincide with clock ticks. For more information on clock resolution and the waiting time, see the Sleep function from the Windows system APIs.
This method does not perform standard COM and SendMessage pumping.
Note
If you need to sleep on a thread that has STAThreadAttribute, but you want to perform standard COM and SendMessage pumping, consider using one of the overloads of the Join method that specifies a timeout interval.
Sleep(TimeSpan)
Suspends the current thread for the specified amount of time.
Parameters
-
timeout
- TimeSpan
The amount of time for which the thread is suspended. If the value of the argument is Zero, the thread relinquishes the remainder of its time slice to any thread of equal priority that is ready to run. If there are no other threads of equal priority that are ready to run, execution of the current thread is not suspended.
Exceptions
ArgumentOutOfRangeException
The value of is negative and is not equal to Infinite in milliseconds, or is greater than MaxValue milliseconds.
Examples
The following example uses the method overload to block the application’s main thread five times, for two seconds each time.
Remarks
The thread will not be scheduled for execution by the operating system for the amount of time specified. This method changes the state of the thread to include .
You can specify Timeout.InfiniteTimeSpan for the parameter to suspend the thread indefinitely. However, we recommend that you use other System.Threading classes such as Mutex, Monitor, EventWaitHandle, or Semaphore instead to synchronize threads or manage resources.
This overload of Sleep uses the total number of whole milliseconds in . Fractional milliseconds are discarded.
This method does not perform standard COM and SendMessage pumping.
Note
If you need to sleep on a thread that has STAThreadAttribute, but you want to perform standard COM and SendMessage pumping, consider using one of the overloads of the Join method that specifies a timeout interval.
Вызов sleep() с декораторами
В некоторых случаях нужно повторно запустить неудачно выполненную в первый раз функцию. Зачастую это происходит, когда требуется повторить загрузку файла ввиду ранней перегрузки сервера. Как правило, никто не хочет делать частые запросы на серверы, поэтому добавление в Python вызова между каждым запросом предпочтительно.
Другим возможным случаем использования является необходимость проверки состояния пользовательского интерфейса во время автоматического теста. В зависимости от компьютера, на котором запускается тест, пользовательский интерфейс может грузиться быстрее или медленнее обычного. Это может изменить отображаемое на экране во время проверки программой чего-то.
В данном случае можно указать программе, чтобы та погрузилась в сон на мгновенье и затем проверить все опять через несколько секунд. Это может означать разницу между прохождением или провалом теста.
Для добавления системного вызова в Python можно использовать декоратор в каждом из данных случаев. Разберем следующий пример:
Python
import time
import urllib.request
import urllib.error
def sleep(timeout, retry=3):
def the_real_decorator(function):
def wrapper(*args, **kwargs):
retries = 0
while retries < retry:
try:
value = function(*args, **kwargs)
if value is None:
return
except:
print(f’Сон на {timeout} секунд’)
time.sleep(timeout)
retries += 1
return wrapper
return the_real_decorator
1 |
importtime importurllib.request importurllib.error defsleep(timeout,retry=3) defthe_real_decorator(function) defwrapper(*args,**kwargs) retries= whileretries<retry try value=function(*args,**kwargs) ifvalue isNone return except print(f’Сон на {timeout} секунд’) time.sleep(timeout) retries+=1 returnwrapper returnthe_real_decorator |
является вашим декоратором. Он принимает значение и количество раз для повтора , что по умолчанию равняется 3. Внутри есть другая функция, , которая принимает декорируемую функцию.
В конечном итоге самая внутренняя функция принимает аргументы и ключевые слова, которые вы передаете декорируемой функции. Здесь все и происходит! Используется , чтобы повторить вызов функции. Если возникла ошибка, вызывается , увеличивается счетчик попыток и повторяется попытка запуска функции.
Теперь переписывается для использования нового декоратора:
Python
@sleep(3)
def uptime_bot(url):
try:
conn = urllib.request.urlopen(url)
except urllib.error.HTTPError as e:
# Отправка admin / log
print(f’HTTPError: {e.code} для {url}’)
# Повторное поднятие ошибки исключения для декоратора
raise urllib.error.HTTPError
except urllib.error.URLError as e:
# Отправка admin / log
print(f’URLError: {e.code} для {url}’)
# Повторное поднятие ошибки исключения для декоратора
raise urllib.error.URLError
else:
# Сайт поднят
print(f'{url} поднят’)
if __name__ == ‘__main__’:
url = ‘http://www.google.com/py’
uptime_bot(url)
2 |
defuptime_bot(url) try conn=urllib.request.urlopen(url) excepturllib.error.HTTPError ase # Отправка admin / log print(f’HTTPError: {e.code} для {url}’) # Повторное поднятие ошибки исключения для декоратора raiseurllib.error.HTTPError excepturllib.error.URLError ase # Отправка admin / log print(f’URLError: {e.code} для {url}’) # Повторное поднятие ошибки исключения для декоратора raiseurllib.error.URLError else # Сайт поднят print(f'{url} поднят’) if__name__==’__main__’ url=’http://www.google.com/py’ uptime_bot(url) |
Здесь вы декорируете с помощью в 3 секунды. Вы также удалили оригинальный цикл и старый вызов . Декоратор теперь позаботится об этом.
Другое изменение состоит в добавлении внутри блоков, отвечающих за обработку исключений. Это нужно для правильной работы декоратора. Можно также написать декоратор, чтобы он отвечал за ошибки, однако ввиду того, что исключения касаются только , может быть лучше сохранить декоратор в текущем состоянии. В таком случае он будет работать c более широким ассортиментом функций.
Декоратору можно добавить несколько улучшений. Если число попыток заканчивается, и он по-прежнему проваливается, тогда можно сделать так, чтобы он повторно вызвал последнюю ошибку. Декоратор подождет 3 секунды после последней неудачи, что не всегда нужно. Можете попробовать поэкспериментировать самостоятельно.
Файл — это очень уж странный предмет…
Системные вызовы — это не только способ попросить ядро обратиться к оборудованию от имени процесса. Это ещё и универсальное API, понятное всем библиотекам в системе. Значит, если в библиотеке не поддержали нужную вам функциональность, возможно, она автоматически получится, если правильно попросить ядро. К тому же, часть «настроек» процесса наследуется при , поэтому таким образом можно попробовать обойтись без сложных костылей, просто правильно сформировав состояние перед запуском процесса (что-то вроде «зачем вручную перекладывать в файл, если можно просто открыть файл и сделать его FD #2 для дочернего процесса»).
Как-то раз мне потребовалось вычитать из файла последовательность сетевых пакетов. В какой-то момент количество костылей превысило все разумные пределы, и я решил, что вряд ли будет сложнее, чем то, что я написал, к тому же, это стандарт, и для открытия этих файлов существуют общепринятые инструменты. Оказалось, что пользоваться для чтения дампов примерно настолько же сложно, как и для чтения файлов: вы просто открываете дамп с помощью и вычерпываете пакеты через . Всё! Ну, ещё стоит закрыть дамп по завершению работы…
Но вот незадача: похоже, не умеет читать из памяти. Может, и умеет конечно, если покопаться, но для нашей «лабораторки» представим, что не умеет.
Итак, модельный пример: мы ждём на некую последовательность байтов, после которой идёт выровненный на 4 байта дамп. Я понимаю, что можно использовать буферизованный ввод и какой-нибудь (поскольку всё равно требует ), но в общем случае мы, может, на ходу распаковываем, например, или библиотека может непосредственно работать с / .
Решение 1: memfd_create
Системный вызов позволяет создать «вообще анонимный» файловый дескриптор. Файл находится в памяти и существует, пока на него открыт хоть один дескриптор. В простейшем случае, вы просто получаете такой дескриптор, записываете в него данные через , перематываете , и с помощью даёте о нём знать :
Имя файла, передаваемое первым аргументом, будет отображаться в символьной ссылке в :
Решение 2: open с флагом O_TMPFILE
В Linux, начиная с какой-то версии, при создании файла можно и имя каталога вместо имени файла. В итоге файл, как (приблизительно) говаривал один литературный персонаж, вроде он есть, но его как бы нет… Пишутся ли данные на диск — не знаю, но наверное, это зависит и от файловой системы (кстати, она должна поддерживать этот режим). Файл всё так же исчезает при закрытии последней ссылки, но его можно прикрепить к дереву каталогов с помощью :
Кроме возможности не мучаться с именованием файла, это даёт возможность заполнить файл, настроить права и т. д., а потом атомарно прилинковать в дерево каталогов.
Вызов sleep() в Tkinter и wxPython
Вызовы в Python можно добавить не только для приложений командной строки. При создании графического пользовательского интерфейса (GUI) периодически нужно добавлять отсрочки. К примеру, при создании приложения FTP для скачивания около миллиона файлов будет разумно добавить вызов между партиями, чтобы снизить нагрузку на сервер.
GUI код выполнит всю обработку в основном потоке, называемом циклом обработки событий, или event loop. При использовании внутри кода GUI заблокируется цикл обработки событий.
К счастью, помимо , можно использовать некоторые другие методы специально для этой задачи. Далее мы рассмотрим, как добавить вызовы в Tkinter и wxPython.
Sleep Until a TimePoint
Many times we want the thread to sleep untill a time point in future. That can be acieved using sleep_untill() i.e.
template< class Clock, class Duration > void sleep_until( const std::chrono::time_point<Clock,Duration>& sleepTime );
Checkout the complete example, here we will put a thread to sleep until a time point in future i.e.
#include <iostream> #include <thread> #include <chrono> // Print Current Time void print_time_point(std::chrono::system_clock::time_point timePoint) { std::time_t timeStamp = std::chrono::system_clock::to_time_t(timePoint); std::cout << std::ctime(&timeStamp) << std::endl; } void threadFunc() { std::cout<<"Current Time :: "; // Print Current Time print_time_point(std::chrono::system_clock::now()); // create a time point pointing to 10 second in future std::chrono::system_clock::time_point timePoint = std::chrono::system_clock::now() + std::chrono::seconds(10); std::cout << "Going to Sleep Until :: "; print_time_point(timePoint); // Sleep Till specified time point // Accepts std::chrono::system_clock::time_point as argument std::this_thread::sleep_until(timePoint); std::cout<<"Current Time :: "; // Print Current Time print_time_point(std::chrono::system_clock::now()); } int main() { std::thread th(&threadFunc); th.join(); return 0; }
Output:
Current Time :: Sat Feb 25 16:44:40 2017 Going to Sleep Until :: Sat Feb 25 16:44:50 2017 Current Time :: Sat Feb 25 16:44:50 2017
SIGTERM vs SIGKILL: Почему вы должны использовать SIGTERM вместо SIGKILL?
Хотя оба эти сигнала используются для уничтожения процесса, между ними есть некоторые различия:
- SIGTERM грациозно убивает процесс, тогда как SIGKILL немедленно убивает процесс.
- Сигнал SIGTERM может обрабатываться, игнорироваться и блокироваться, но SIGKILL не может быть обработан или заблокирован.
- SIGTERM не убивает дочерние процессы. SIGKILL также убивает дочерние процессы.
Некоторые пользователи Linux привыкли использовать kill -9, и этого следует избегать. Если у вас нет процесса без ответа, вам не нужно использовать SIGKILL.
С помощью SIGTERM процесс получает время для отправки информации своим родительским и дочерним процессам. Это дочерние процессы обрабатываются init.
Использование SIGKILL может привести к созданию процесса зомби, потому что уничтоженный процесс не получает возможности сообщить своему родительскому процессу, что он получил сигнал уничтожения.
Мы надеемся, что вы теперь лучше понимаете, как убить процесс с помощью SIGTERM или SIGKILL.
Если у вас есть какие-либо вопросы или предложения, пожалуйста, не стесняйтесь оставлять комментарии.
Использование Queues
Очередь(Queues Python) может быть использована для стековых реализаций «пришел первым – ушел первым» (first-in-first-out (FIFO)) или же «пришел последним – ушел последним» (last-in-last-out (LILO)) , если вы используете их правильно.
В данном разделе, мы смешаем потоки и создадим простой скрипт файлового загрузчика, чтобы продемонстрировать, как работает Queues Python со случаями, которые мы хотим паралеллизировать. Чтобы помочь объяснить, как работает Queues, мы перепишем загрузочный скрипт из предыдущей секции для использования Queues. Приступим!
Python
# -*- coding: utf-8 -*-
import os
import threading
import urllib.request
from queue import Queue
class Downloader(threading.Thread):
«»»Потоковый загрузчик файлов»»»
def __init__(self, queue):
«»»Инициализация потока»»»
threading.Thread.__init__(self)
self.queue = queue
def run(self):
«»»Запуск потока»»»
while True:
# Получаем url из очереди
url = self.queue.get()
# Скачиваем файл
self.download_file(url)
# Отправляем сигнал о том, что задача завершена
self.queue.task_done()
def download_file(self, url):
«»»Скачиваем файл»»»
handle = urllib.request.urlopen(url)
fname = os.path.basename(url)
with open(fname, «wb») as f:
while True:
chunk = handle.read(1024)
if not chunk:
break
f.write(chunk)
def main(urls):
«»»
Запускаем программу
«»»
queue = Queue()
# Запускаем потом и очередь
for i in range(5):
t = Downloader(queue)
t.setDaemon(True)
t.start()
# Даем очереди нужные нам ссылки для скачивания
for url in urls:
queue.put(url)
# Ждем завершения работы очереди
queue.join()
if __name__ == «__main__»:
urls = [«http://www.irs.gov/pub/irs-pdf/f1040.pdf»,
«http://www.irs.gov/pub/irs-pdf/f1040a.pdf»,
«http://www.irs.gov/pub/irs-pdf/f1040ez.pdf»,
«http://www.irs.gov/pub/irs-pdf/f1040es.pdf»,
«http://www.irs.gov/pub/irs-pdf/f1040sb.pdf»]
main(urls)
1 |
# -*- coding: utf-8 -*- importos importthreading importurllib.request fromqueueimportQueue classDownloader(threading.Thread) «»»Потоковый загрузчик файлов»»» def__init__(self,queue) «»»Инициализация потока»»» threading.Thread.__init__(self) self.queue=queue defrun(self) «»»Запуск потока»»» whileTrue # Получаем url из очереди url=self.queue.get() # Скачиваем файл self.download_file(url) # Отправляем сигнал о том, что задача завершена self.queue.task_done() defdownload_file(self,url) «»»Скачиваем файл»»» handle=urllib.request.urlopen(url) fname=os.path.basename(url) withopen(fname,»wb»)asf whileTrue chunk=handle.read(1024) ifnotchunk break f.write(chunk) defmain(urls) «»» Запускаем программу queue=Queue() # Запускаем потом и очередь foriinrange(5) t=Downloader(queue) t.setDaemon(True) t.start() # Даем очереди нужные нам ссылки для скачивания forurl inurls queue.put(url) # Ждем завершения работы очереди queue.join() if__name__==»__main__» urls=»http://www.irs.gov/pub/irs-pdf/f1040.pdf», «http://www.irs.gov/pub/irs-pdf/f1040a.pdf», «http://www.irs.gov/pub/irs-pdf/f1040ez.pdf», «http://www.irs.gov/pub/irs-pdf/f1040es.pdf», «http://www.irs.gov/pub/irs-pdf/f1040sb.pdf» main(urls) |
Давайте притормозим. В первую очередь, нам нужно взглянуть на определение главной функции для того, чтобы увидеть, как все протекает. Здесь мы видим, что она принимает список url адресов. Далее, функция main создаете экземпляр очереди, которая передана пяти демонизированным потокам. Основная разница между демонизированным и недемонизированным потоком в том, что вам нужно отслеживать недемонизированные потоки и закрывать их вручную, в то время как поток «демон» нужно только запустить и забыть о нем. Когда ваше приложение закроется, закроется и поток. Далее мы загрузили очередь (при помощи метода put) вместе с переданными url. Наконец, мы указываем очереди подождать, пока потоки выполнят свои процессы через метод join. В классе download у нас есть строчка self.queue.get(), которая выполняет функцию блока, пока очередь делает что-либо для возврата. Это значит, что потоки скромно будут дожидаться своей очереди. Также это значит, чтобы поток получал что-нибудь из очереди, он должен вызывать метод очереди под названием get. Таким образом, добавляя что-нибудь в очередь, пул потоков, поднимет или возьмет эти объекты и обработает их. Это также известно как dequeing. После того, как все объекты в очередь обработаны, скрипт заканчивается и закрывается. На моем компьютере были загружены первые 5 документов за секунду.
BUGS top
If a program that catches signals and uses nanosleep() receives signals at a very high rate, then scheduling delays and rounding errors in the kernel's calculation of the sleep interval and the returned remain value mean that the remain value may steadily increase on successive restarts of the nanosleep() call. To avoid such problems, use clock_nanosleep(2) with the TIMER_ABSTIME flag to sleep to an absolute deadline. In Linux 2.4, if nanosleep() is stopped by a signal (e.g., SIGTSTP), then the call fails with the error EINTR after the thread is resumed by a SIGCONT signal. If the system call is subsequently restarted, then the time that the thread spent in the stopped state is not counted against the sleep interval. This problem is fixed in Linux 2.6.0 and later kernels.