Багатопотоковість в Python

14 хв. читання

Запуск декількох потоків аналогічний запуску декількох програм, але з декількома перевагами:

  • Кілька потоків всередині одного процесу займають один простір даних з головним потоком і завдяки цьому їм набагато легше взаємодіяти та обмінюватися даними між собою, ніж якби вони були окремими процесами;
  • Потоки іноді ще називають "легкими" процесами, оскільки їм не потрібно багато пам'яті;
  • Потік, має початок, послідовність виконання та закінчення. Він має покажчик, який відстежує де в його контексті він працює;
  • Він може бути перерваний;
  • Він може бути тимчасово призупиненим, поки інші потоки працюють - така властивість називається поступання.

Початок нового потоку

Для того щоб викликати потік, потрібно викликати метод, який доступний в модулі Thread:

`**thread.start_new_thread( function, args[, kwargs] )**`  

Саме виклик цього методу дозволяє швидко та ефективно створювати нові потоки. Виклик методу повертається негайно, починається "дитячий" потік і викликається функція з минулим списком аргументів. Коли функція повертається, потік завершується. Тут аргументи подані у вигляді списку аргументів. Використовувати пустий список, означає викликати функції без аргументів.

Приклад:

    #!/usr/bin/python
    
    import thread
    import time
    
    # Визначити функцію для потоку
    def print_time( threadName, delay):
       count = 0
       while count < 5:
          time.sleep(delay)
          count += 1
          print "%s: %s" % ( threadName, time.ctime(time.time()) )
    
    # Створити два потоки наступним чином
    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

Результати виконання програми:

Thread-1: Thu Jan 22 15:42:17 2009  
Thread-1: Thu Jan 22 15:42:19 2009  
Thread-2: Thu Jan 22 15:42:19 2009  
Thread-1: Thu Jan 22 15:42:21 2009  
Thread-2: Thu Jan 22 15:42:23 2009  
Thread-1: Thu Jan 22 15:42:23 2009  
Thread-1: Thu Jan 22 15:42:25 2009  
Thread-2: Thu Jan 22 15:42:27 2009  
Thread-2: Thu Jan 22 15:42:31 2009  
Thread-2: Thu Jan 22 15:42:35 2009

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

Потоковий модуль

Більш новий потоковий модуль включений до складу Python 2.4 і він надає набагато потужнішу підтримку потоків високого рівня, ніж той модуль, який ми розглядали у попередньому розділі. Цей модуль включає все, що було у старому модулі і додає деякі додаткові методи:

  • threading.activeCount(): Повертає кількість об'єктів потоку, які активні;
  • threading.currentThread(): Повертає кількість об'єктів потоку у викликаних thread control;
  • **threading.enumerate():**Повертає список кількості потоків, які на даний момент активні.

На додаток до методів, модуль має клас Thread, який як ви здогадалися реалізує самі потоки. Клас має такі методи:

  • run(): метод run(), є методом початку потоку;
  • **start()**: є методом запуску потоку після виклику методу run();
  • join([time]): чекає закінчення виконання потоку;
  • isAlive(): перевіряє чи виконується потік;
  • getName(): повертає ім'я потоку;
  • setName(): встановлює ім'я потоку.

Створення потоку використовуючи Threading Module

Для реалізації нового потоку за допомогою Threading Module, вам потрібно виконати наступне:

  1. Визначити новий підклас класу Thread;
  2. Перегрузити метод **__init__(self[,args])**, для додавання нових аргументів;
  3. Тоді, перевизначити метод **Run(self [, args])** для того, щоб визначити, що потік повинен робити, коли стартує;
  4. Після того як ви створили новий підклас потоку, ви можете створити його екземпляр, а потім почати запустити потік, викликавши метод **start()**.

Приклад:

    #!/usr/bin/python
    
    import threading
    import time
    
    exitFlag = 0
    
    class myThread (threading.Thread):
        def __init__(self, threadID, name, counter):
            threading.Thread.__init__(self)
            self.threadID = threadID
            self.name = name
            self.counter = counter
        def run(self):
            print "Starting " + self.name
            print_time(self.name, self.counter, 5)
            print "Exiting " + self.name
    
    def print_time(threadName, delay, counter):
        while counter:
            if exitFlag:
                thread.exit()
            time.sleep(delay)
            print "%s: %s" % (threadName, time.ctime(time.time()))
            counter -= 1
    
    # Створюємо нові потоки
    thread1 = myThread(1, "Thread-1", 1)
    thread2 = myThread(2, "Thread-2", 2)
    
    # Запускаємо нові потоки
    thread1.start()
    thread2.start()
    
    print "Exiting Main Thread"

Результат:

Starting Thread-1  
Starting Thread-2  
Exiting Main Thread  
Thread-1: Thu Mar 21 09:10:03 2013  
Thread-1: Thu Mar 21 09:10:04 2013  
Thread-2: Thu Mar 21 09:10:04 2013  
Thread-1: Thu Mar 21 09:10:05 2013  
Thread-1: Thu Mar 21 09:10:06 2013  
Thread-2: Thu Mar 21 09:10:06 2013  
Thread-1: Thu Mar 21 09:10:07 2013  
Exiting Thread-1  
Thread-2: Thu Mar 21 09:10:08 2013  
Thread-2: Thu Mar 21 09:10:10 2013  
Thread-2: Thu Mar 21 09:10:12 2013  
Exiting Thread-2

Синхронізація потоків

В Python потоковий модуль включає в себе простий у використанні механізм блокування, за допомогою якого можна синхронізувати потоки. Новий замок(Lock) можна створити викликавши метод **Lock()**.

Необов'язковий параметр блокування дозволяє контролювати, чи буде потік чекати, щоб отримати блокування. Якщо на блокуванні встановлено 0, то потік безпосередньо буде повертатися зі значенням 0. Якщо встановлено значення 1, то потік буде заблокований і чекатиме звільнення блокування.

Метод **release()** нового об'єкта блокування, буде використвовуватись для зняття блокування, коли воно не потрібно.

Приклад:

    #!/usr/bin/python
    
    import threading
    import time
    
    class myThread (threading.Thread):
        def __init__(self, threadID, name, counter):
            threading.Thread.__init__(self)
            self.threadID = threadID
            self.name = name
            self.counter = counter
        def run(self):
            print "Starting " + self.name
            # Отримуємо замок для синхронізації потоків
            threadLock.acquire()
            print_time(self.name, self.counter, 3)
            # Відкритий замок для реалізування наступного потоку
            threadLock.release()
    
    def print_time(threadName, delay, counter):
        while counter:
            time.sleep(delay)
            print "%s: %s" % (threadName, time.ctime(time.time()))
            counter -= 1
    
    threadLock = threading.Lock()
    threads = []
    
    # Створюємо нові потоки
    thread1 = myThread(1, "Thread-1", 1)
    thread2 = myThread(2, "Thread-2", 2)
    
    # Запускаємо нові потоки
    thread1.start()
    thread2.start()
    
    # Додаємо потоки до списку потоків
    threads.append(thread1)
    threads.append(thread2)
    
    # Чекємо завершення всіх потоків
    for t in threads:
        t.join()
    print "Exiting Main Thread"

Результат:

Starting Thread-1  
Starting Thread-2  
Thread-1: Thu Mar 21 09:11:28 2013  
Thread-1: Thu Mar 21 09:11:29 2013  
Thread-1: Thu Mar 21 09:11:30 2013  
Thread-2: Thu Mar 21 09:11:32 2013  
Thread-2: Thu Mar 21 09:11:34 2013  
Thread-2: Thu Mar 21 09:11:36 2013  
Exiting Main Thread

Багатопотокова пріоритетна черга

Модуль Queue дозволяє створити новий об'єкт черги, який може містити певну кількість пунктів. Існують наступні способи управління чергою:

  • **get():**видаляє і повертає елементи з черги;
  • put(): додає елементи в чергу;
  • qsize(): повертає кількість елементів, які в даний час в черзі;
  • empty(): повертає True, якщо черга порожня, в іншому випадку - False;
  • **full():**повертає True, якщо черга заповнена, в в іншому випадку - False.

Приклад:

    #!/usr/bin/python
    
    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 = ["Thread-1", "Thread-2", "Thread-3"]
    nameList = ["One", "Two", "Three", "Four", "Five"]
    queueLock = threading.Lock()
    workQueue = Queue.Queue(10)
    threads = []
    threadID = 1
    
    # Створюємо нові потоки
    for tName in threadList:
        thread = myThread(threadID, tName, workQueue)
        thread.start()
        threads.append(thread)
        threadID += 1
    
    # Заповнюємо чергу
    queueLock.acquire()
    for word in nameList:
        workQueue.put(word)
    queueLock.release()
    
    # Чекаємо порожню чергу
    while not workQueue.empty():
        pass
    
    # "Нагадуємо" потокам про час закінчення
    exitFlag = 1
    
    # Чекаємо завершення всіх потоків
    for t in threads:
        t.join()
    print "Exiting Main Thread"

Результат:

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
Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.2K
Приєднався: 9 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація