Опубликован: 22.12.2005 | Доступ: свободный | Студентов: 16816 / 518 | Оценка: 4.18 / 3.71 | Длительность: 16:16:00
ISBN: 978-5-9556-0109-0
Лекция 11:

Многопоточные вычисления

< Лекция 10 || Лекция 11: 12345 || Лекция 12 >

Модуль thread

По сравнению с модулем threading, модуль thread предоставляет низкоуровневый доступ к потокам. Многие функции модуля threading, который рассматривался до этого, реализованы на базе модуля thread. Здесь стоит сделать некоторые замечания по применению потоков вообще. Документация по Python предупреждает, что использование потоков имеет особенности:

  • Исключение KeyboardInterrupt (прерывание от клавиатуры) может быть получено любым из потоков, если в поставке Python нет модуля signal (для обработки сигналов).
  • Не все встроенные функции, блокированные ожиданием ввода, позволяют другим потокам работать. Правда, основные функции вроде time.sleep(), select.select(), метод read() файловых объектов не блокируют другие потоки.
  • Невозможно прервать метод acquire(), так как исключение KeyboardInterrupt возбуждается только после возврата из этого метода.
  • Нежелательно, чтобы главный поток завершался раньше других потоков, так как не будут выполнены необходимые деструкторы и даже части finally в операторах try-finally. Это связано с тем, что почти все операционные системы завершают приложение, у которого завершился главный поток.

Визуализация работы потоков

Следующий пример иллюстрирует параллельность выполнения потоков, используя возможности библиотеки графических примитивов Tkinter (она входит в стандартную поставку Python). Несколько потоков наперегонки увеличивают размеры прямоугольника некоторого цвета. Цветом победившего потока окрашивается кнопка Go:

import threading, time, sys
            from Tkinter import Tk, Canvas, Button, LEFT, RIGHT, NORMAL, DISABLED

            global champion

            # Задается дистанция, цвет полосок и другие параметры
            distance = 300
            colors = ["Red","Orange","Yellow","Green","Blue","DarkBlue","Violet"]
            nrunners = len(colors)      # количество дополнительных потоков
            positions = [0] * nrunners  # список текущих позиций
            h, h2 = 20, 10              # параметры высоты полосок

            def run(n):
              """Программа бега n-го участника (потока)"""
              global champion
              while 1:
                for i in range(10000):           # интенсивные вычисления
                  pass
                graph_lock.acquire()
                positions[n] += 1                # передвижение на шаг
                if positions[n] == distance:     # если уже финиш
                  if champion is None:           # и чемпион еще не определен,
                    champion = colors[n]         # назначается чемпион
                  graph_lock.release()
                  break
                graph_lock.release()

            def ready_steady_go():
              """Инициализация начальных позиций и запуск потоков"""
              graph_lock.acquire()
              for i in range(nrunners):
                positions[i] = 0
                threading.Thread(target=run, args=[i,]).start()
              graph_lock.release()

            def update_positions():
              """Обновление позиций"""
              graph_lock.acquire()
              for n in range(nrunners):
                c.coords(rects[n], 0, n*h, positions[n], n*h+h2)
              tk.update_idletasks()  # прорисовка изменений
              graph_lock.release()

            def quit():
              """Выход из программы"""
              tk.quit()
              sys.exit(0)

            # Прорисовка окна, основы для прямоугольников и самих прямоугольников,
            # кнопок для пуска и выхода
            tk = Tk()
            tk.title("Соревнование потоков")
            c = Canvas(tk, width=distance, height=nrunners*h, bg="White")
            c.pack()
            rects = [c.create_rectangle(0, i*h, 0, i*h+h2, fill=colors[i])
                     for i in range(nrunners)]
            go_b = Button(text="Go", command=tk.quit)
            go_b.pack(side=LEFT)
            quit_b = Button(text="Quit", command=quit)
            quit_b.pack(side=RIGHT)

            # Замок, регулирующий доступ к функции пакета Tk
            graph_lock = threading.Lock()

            # Цикл проведения соревнований
            while 1:
              go_b.config(state=NORMAL), quit_b.config(state=NORMAL)
              tk.mainloop()             # Ожидание нажатия клавиш
              champion = None
              ready_steady_go()
              go_b.config(state=DISABLED), quit_b.config(state=DISABLED)
              # Главный поток ждет финиша всех участников
              while sum(positions) < distance*nrunners:
                update_positions()
              update_positions()
              go_b.config(bg=champion)     # Кнопка окрашивается в цвет победителя
              tk.update_idletasks()

Примечание:

Эта программа использует некоторые возможности языка Python 2.3 (встроенную функцию sum() и списковые включения), поэтому для ее выполнения нужен Python версии не меньше 2.3.

Заключение

Навыки параллельного программирования необходимы любому профессиональному программисту. Одним из вариантов организации (псевдо) параллельного программирования является многопоточное программирование (другой вариант, более свойственный Unix-системам - многопроцессное программирование - здесь не рассматривается). В обычной (однопоточной) программе действует всего один поток управления, а в многопоточной одновременно могут работать несколько потоков.

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

Стандартная библиотека Python предоставляет довольно неплохой набор возможностей для многопоточного программирования в модулях threading и thread, а также некоторые полезные вспомогательные модули (например, Queue ).

< Лекция 10 || Лекция 11: 12345 || Лекция 12 >
Андрей Егоров
Андрей Егоров

def bin(n):

"""Цифры двоичного представления натурального числа """

if n == 0:

   return []

n, d = divmod(n, 2)

return bin(n) + [d]

print bin(69)

Что значит здесь return[] ? Возвращает список? Непонятно какой список? Откуда он? 

 

 

Асмик Гаряка
Асмик Гаряка

Почему при вычислении рейтинга не учитывается уровень, как описано? Для всех курсов У=1, хотя для Специалист должно быть 2.

Игорь Хан
Игорь Хан
Узбекистан, Ташкент, Ташкентский педагогический институт иностранных языков, 1990
Владислав Третьяков
Владислав Третьяков
Россия, г. Омск