Заметки python #23: потоки, процессы

Applications

  • Introduction into the sys module
  • Python and the Shell
  • Forks and Forking in Python
  • Introduction into Threads
  • Pipe, Pipes and «99 Bottles of Beer»
  • Python Network Scanner
  • Graph Theory and Graphs in Python
  • Graphs: PyGraph
  • Graphs
  • A Python Class for Polynomial Functions
  • Finite State Machine in Python
  • Turing Machine in Python
  • Levenshtein Distance
  • Verbalize Time in Turkish
  • Example for recursive Programming: Towers of Hanoi
  • Mastermind / Bulls and Cows
  • Creating dynamic websites with WSGI
  • Dynamic websites with mod_python
  • Dynamic websites with Pylons
  • Python, SQL, MySQL and SQLite
  • Python Scores

Starting a New Thread

To spawn another thread, you need to call the following method available in the thread module −


_thread.start_new_thread ( function, args )

This method call enables a fast and efficient way to create new threads in both Linux and Windows.

The method call returns immediately and the child thread starts and calls function with the passed list of args. When the function returns, the thread terminates.

Here, args is a tuple of arguments; use an empty tuple to call function without passing any arguments. kwargs is an optional dictionary of keyword arguments.

Example

#!/usr/bin/python3

import _thread
import time

# Define a function for the thread
def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print ("%s: %s" % ( threadName, time.ctime(time.time()) ))

# Create two threads as follows
try:
   _thread.start_new_thread( print_time, ("Thread-1", 2, ) )
   _thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
   print ("Error: unable to start thread")

while 1:
   pass

Output

When the above code is executed, it produces the following result −

Thread-1: Fri Feb 19 09:41:39 2016
Thread-2: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:43 2016
Thread-2: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:47 2016
Thread-2: Fri Feb 19 09:41:49 2016
Thread-2: Fri Feb 19 09:41:53 2016

Program goes in an infinite loop. You will have to press ctrl-c to stop

Although it is very effective for low-level threading, the thread module is very limited compared to the newer threading module.

Usage

ThreadPooled

Mostly it is required decorator: submit function to ThreadPoolExecutor on call.

Note

API quite differs between Python 3 and Python 2.7. See API section below.

threaded.ThreadPooled.configure(max_workers=3)

Note

By default, if executor is not configured — it configures with default parameters: max_workers=CPU_COUNT * 5

@threaded.ThreadPooled
def func():
    pass

concurrent.futures.wait()

Python 3.5+ usage with asyncio:

Note

if loop_getter is not callable, loop_getter_need_context is ignored.

loop = asyncio.get_event_loop()
@threaded.ThreadPooled(loop_getter=loop, loop_getter_need_context=False)
def func():
    pass

loop.run_until_complete(asyncio.wait_for(func(), timeout))

Python 3.5+ usage with asyncio and loop extraction from call arguments:

loop_getter = lambda tgt_loop tgt_loop
@threaded.ThreadPooled(loop_getter=loop_getter, loop_getter_need_context=True)  # loop_getter_need_context is required
def func(*args, **kwargs):
    pass

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait_for(func(loop), timeout))

During application shutdown, pool can be stopped (while it will be recreated automatically, if some component will request).

threaded.ThreadPooled.shutdown()

Threaded

Classic threading.Thread. Useful for running until close and self-closing threads without return.

Usage example:

@threaded.Threaded
def func(*args, **kwargs):
    pass

thread = func()
thread.start()
thread.join()

Without arguments, thread name will use pattern: 'Threaded: ' + func.__name__

Note

If func.__name__ is not accessible, str(hash(func)) will be used instead.

Override name can be don via corresponding argument:

@threaded.Threaded(name='Function in thread')
def func(*args, **kwargs):
    pass

Thread can be daemonized automatically:

@threaded.Threaded(daemon=True)
def func(*args, **kwargs):
    pass

Also, if no any addition manipulations expected before thread start, it can be started automatically before return:

@threaded.Threaded(started=True)
def func(*args, **kwargs):
    pass

Multithreaded Priority Queue

The Queue module allows you to create a new queue object that can hold a specific number of items. There are following methods to control the Queue −

  • get() − The get() removes and returns an item from the queue.

  • put() − The put adds item to a queue.

  • qsize() − The qsize() returns the number of items that are currently in the queue.

  • empty() − The empty( ) returns True if queue is empty; otherwise, False.

  • full() − the full() returns True if queue is full; otherwise, False.

Example

#!/usr/bin/python3

import queue
import threading
import time

exitFlag = 0

class myThread (threading.Thread):
   def __init__(self, threadID, name, q):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.q = q
   def run(self):
      print ("Starting " + self.name)
      process_data(self.name, self.q)
      print ("Exiting " + self.name)

def process_data(threadName, q):
   while not exitFlag:
      queueLock.acquire()
      if not workQueue.empty():
         data = q.get()
         queueLock.release()
         print ("%s processing %s" % (threadName, data))
      else:
         queueLock.release()
         time.sleep(1)

threadList = 
nameList = 
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1

# Create new threads
for tName in threadList:
   thread = myThread(threadID, tName, workQueue)
   thread.start()
   threads.append(thread)
   threadID += 1

# Fill the queue
queueLock.acquire()
for word in nameList:
   workQueue.put(word)
queueLock.release()

# Wait for queue to empty
while not workQueue.empty():
   pass

# Notify threads it's time to exit
exitFlag = 1

# Wait for all threads to complete
for t in threads:
   t.join()
print ("Exiting Main Thread")

Output

When the above code is executed, it produces the following result −

Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
Exiting Thread-2
Exiting Main Thread

Previous Page Print Page

Next Page  

8 продвинутых возможностей модуля logging в Python, которые вы не должны пропустить

Перевод

Понимайте свою программу без ущерба для производительности

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

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

Код, написанный на Go, легко поддерживать.

Нет классов. Каждая вещь делится только на пакеты. Вместо классов Go использует только структуры (structs. — прим.перев.).

Не поддерживает наследование. Это упрощает модифицирование кода. В других языках, типа Java/Python, если класс ABC наследует класс XYZ, и вы при этом делаете какие-то изменения в классе XYZ, то это производит дополнительное изменения во всех наследованных от него классах

К тому же, без наследования, код Go становится более простым для понимания (так как нет super классов, на которые нужно обращать внимание при изучении участков кода). Нет конструкторов. Нет аннотаций. Нет дженериков (универсальных шаблонов. — прим.перев.). Нет исключений.

Читабельность кода против его производительности.win-win. — прим.перев.другом новом языке Swift

Что значит «идеальный» пакет?

Буду исходить из следующих требований:

  • Open source на github; Каждый должен иметь возможность внести свой вклад в развитие и поблагодарить автора.
  • Поддержка всех актуальных\популярных версий python (2.7, 3.5, 3.6, 3.7, 3.8); Питоны бывают разные, и где-то до сих пор активно пишут на 2.7.
  • Использование CI: Автоматические проверки — это очень удобно! А еще куча клевых бейджей
    • Проверка код стайла; Единый стиль улучшает читаемость и уменьшает количество пустых дискуссий в ревью.
    • Статический анализатора кода; Автоматический поиск багов в коде? Дайте два!
  • Актуальная документация; Примеры работы с пакетом, описание методов\классов и разбор типичных ошибок — задокументированный опыт позволят снизить порог входа для новичков.
  • Кроссплатформенность разработки; К сожалению, в проекты сложно вносить личный вклад просто потому, что разработчик заточил инструменты под Unix. К примеру, для сборки использовал bash скрипты.

Класс Thread

В Java функциональность отдельного потока заключается в классе Thread. И чтобы создать новый поток, нам надо создать объект этого класса. Но все потоки не создаются сами по себе. Когда запускается программа, начинает работать главный поток этой программы. От этого главного потока порождаются все остальные дочерние потоки.

С помощью статического метода Thread.currentThread() мы можем получить текущий поток выполнения:

public static void main(String[] args) {
        
    Thread t = Thread.currentThread(); // получаем главный поток
    System.out.println(t.getName()); // main
}

По умолчанию именем главного потока будет .

Для управления потоком класс Thread предоставляет еще ряд методов. Наиболее используемые из них:

  • getName(): возвращает имя потока

  • setName(String name): устанавливает имя потока

  • getPriority(): возвращает приоритет потока

  • setPriority(int proirity): устанавливает приоритет потока. Приоритет является одним из ключевых факторов для выбора системой потока из кучи потоков для выполнения. В этот метод в качестве параметра передается числовое значение приоритета — от 1 до 10. По умолчанию главному потоку выставляется средний приоритет — 5.

  • isAlive(): возвращает true, если поток активен


  • isInterrupted(): возвращает true, если поток был прерван

  • join(): ожидает завершение потока

  • run(): определяет точку входа в поток

  • sleep(): приостанавливает поток на заданное количество миллисекунд

  • start(): запускает поток, вызывая его метод

Мы можем вывести всю информацию о потоке:

public static void main(String[] args) {
        
    Thread t = Thread.currentThread(); // получаем главный поток
    System.out.println(t); // main
}

Консольный вывод:

Thread

Первое будет представлять имя потока (что можно получить через ), второе значение 5 предоставляет приоритет потока (также можно получить через ), и последнее представляет имя группы потоков, к которому относится текущий — по умолчанию также main (также можно получить через )

Недостатки при использовании потоков

Далее мы рассмотрим, как создавать и использовать потоки. Это довольно легко. Однако при создании многопоточного приложения нам следует учитывать ряд обстоятельств, которые негативно могут сказаться на работе приложения.

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

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

НазадВперед

pyOpenRPA туториал. Управление WEB приложениями

Tutorial

Долгожданный туториал по управлению сторонними WEB приложениями с помощью pyOpenRPA. Во 2-й части мы разберем принципы роботизированного воздействия на HTML/JS. А также своими руками сделаем небольшого, но очень показательного робота.

Этот робот будет полезен тем, для кого актуальна тема покупки/продажи недвижимости.

Навигация по туториалам pyOpenRPA

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

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

Перечень статей-туториалов (опубликованные и планируемые):

  • Отказываемся от платных RPA платформ и базируемся на OpenSource (pyOpenRPA)
  • pyOpenRPA туториал. Управление оконными GUI приложениями
  • >> pyOpenRPA туториал. Управление WEB приложениями (то, что мы смотрим в Chrome, Firefox, Opera)
  • pyOpenRPA туториал. Управление клавиатурой & мышью
  • pyOpenRPA туториал. Распознавание графических объектов на экране

А теперь перейдем к самому туториалу.

Как залить на pypi?

Как собрать пакет?

Снова пишу среду, которая поможет мне собрать необходимый пакет:

Зачем я удаляю папки с помощью python? Хочу соблюсти требование о кроссплатформенности разработки, удобного пути сделать это под Windows и Unix нет.

Запускаю тестовую среду:

В результате в папке получаю:

Заливаю!

Не совсем.

Изначально проект назывался , но при попытке заливки столкнулся с тем, что пакет с таким именем уже есть! И что самое обидное — он тоже умеет конвертировать в римские цифры:) Сильно не расстроился и переименовал в .

Теперь нужно установить пакет из тестового окружения. Проверку так же стоит автоматизировать:

Теперь нужно только запустить:


Вроде работает 🙂

Да.

Создаю среду для заливки в production репозиторий.

И среду для production проверки.

Как тестировать?

Делаю следующую структуру проекта:

Тесты в пакет класть не буду, поэтому отделяю зёрна от плевел. Изначально папку не создавал, но дальнейшее развитие показало, что мне так удобнее оперировать. Это не обязательно, поэтому по желанию.

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

Но, поговаривают, что ставить (как и любые другие зависимости) в системный python — не очень умная идея…

Использование потоков

Мы начнем с простого примера, который демонстрирует работу потоков. Мы наследуем класс Thread в класс MyThread и укажем, чтобы его имя выводилось как stdout. Попробуем!

Python

# -*- coding: utf-8 -*- import random import time from threading import Thread

class MyThread(Thread): «»» A threading example «»» def __init__(self, name): «»»Инициализация потока»»» Thread.__init__(self) self.name = name def run(self): «»»Запуск потока»»» amount = random.randint(3, 15) time.sleep(amount) msg = «%s is running» % self.name print(msg) def create_threads(): «»» Создаем группу потоков «»» for i in range(5): name = «Thread #%s» % (i+1) my_thread = MyThread(name) my_thread.start()

if __name__ == «__main__»: create_threads()

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

# -*- coding: utf-8 -*-

importrandom

importtime

fromthreadingimportThread

classMyThread(Thread)

«»»

    A threading example     «»»

def__init__(self,name)

«»»Инициализация потока»»»

Thread.__init__(self)

self.name=name

defrun(self)

«»»Запуск потока»»»

amount=random.randint(3,15)

time.sleep(amount)

msg=»%s is running»%self.name

print(msg)

defcreate_threads()

«»»

    Создаем группу потоков     «»»

foriinrange(5)

name=»Thread #%s»%(i+1)

my_thread=MyThread(name)

my_thread.start()

if__name__==»__main__»

create_threads()

В этом коде мы импортировали модули random и time, также мы импортировали класс Thread из модуля threading Python. Далее, мы  наследуем класс Thread, и переопределили его метод __init__ для принятия аргумента, под названием name. Для начала потока, вам нужно вызывать метод start().

После запуска потока, он автоматически вызовет метод run. Мы переопределили метод run таким образом, чтобы он выбирал случайный отсчет времени для «сна». Пример random.randint указывает Python выбрать случайное число от 3 до 15. После этого мы указываем потоку «спать» столько секунд, сколько было выбрано случайным способом, для симуляции его настоящей работы. Далее мы ввели имя потока, чтобы сказать пользователю, что он закончился. Функция create_threads создаст 5 потоков, дав каждому из них уникальное имя. Если вы запустите данный код, вы увидите что-то вроде этого:

Python

Thread #2 is running Thread #3 is running Thread #1 is running Thread #4 is running Thread #5 is running

1 2 3 4 5

Thread #2 is running Thread #3 is running Thread #1 is running Thread #4 is running Thread #5 is running

Порядок выхода каждый раз будет разным. Попробуйте запустить код несколько раз, чтобы увидеть смену порядка. Теперь давайте напишем что-нибудь более практичное!

Структуры данных

Python содержит такие структуры данных как списки (lists), кортежи (tuples) и словари (dictionaries). Списки — похожи на одномерные массивы (но вы можете использовать Список включающий списки — многомерный массив), кортежи — неизменяемые списки, словари — тоже списки, но индексы могут быть любого типа, а не только числовыми. «Массивы» в Python могут содержать данные любого типа, то есть в одном массиве может могут находиться числовые, строковые и другие типы данных. Массивы начинаются с индекса 0, а последний элемент можно получить по индексу -1 Вы можете присваивать переменным функции и использовать их соответственно.

Вы можете использовать часть массива, задавая первый и последний индекс через двоеточие «:». В таком случае вы получите часть массива, от первого индекса до второго не включительно. Если не указан первый элемент, то отсчет начинается с начала массива, а если не указан последний — то масив считывается до последнего элемента. Отрицательные значения определяют положение элемента с конца. Например:


С этим читают