Как приручить свой Bluetooth. Часть 3

В этой части начнем писать код.

Вопрос в том, на чём писать — какой язык программирования выбрать? Если лет 10-15 назад я мог однозначно ткнуть пальцем с C/C++ и не ошибиться, то сегодня я уже впадаю в ступор.

Казалось бы для решения задач работы с оборудованием подходит только C/C++, а на таком языке как Python можно создать только что-то не очень серьезное. Отчасти это так. Но по нынешним временам компьютеры обладают достаточной вычислительной мощностью и могут без особых лагов (кратковременных зависаний) перемалывать интерпретацию Питоновских кодов.

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

Питоновские программы, как правило, распространяются в виде исходных кодов. Я не делаю секрета из программы, которую мы будем писать. Наоборот! Я говорю

— Ребята! Код открыт и прокомментирован. Изучайте работу программы и развивайте её в своем направлении. Дописывайте к ней тот функционал, который вам необходим. Я даже не претендую на авторство и не навешиваю копирайт. Подхватывайте и несите дальше.

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

Итак, решено — выбираем Python.

Если конкретизировать, то я буду ориентироваться на Python-2x, а не на Python-3x. Я это делаю преднамеренно. Дело в том, что в третьем Питоне есть проблемы с модулем, который работает с последовательным портом. А этот модуль в нашей программе — чуть ли не основной. Ну и кроме того, мне приходят письма, в которых люди задают вопрос «А где можно взять этот модуль для Python-3x?»

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

И перед тем как начать, сделаю одно маленькое замечание по многопоточности Питона. Дело в том, что Python хотя и позволяет создавать многопоточные программы, но самом деле они не истинно многопоточные, потому что выполняются они на одном ядре.

Истинная многопоточная программа всегда задействует все ядра процессора. Многопоточная Питоновская программа всегда выполняется на одном ядре. Я не большой специалист Питона, я не знаю, по какой причине так получилось.

Потоки в Питоновской программе хоть и выполняются параллельно, но процессор (точнее — ядро процессора) не может их выполнять одновременно в одно и то же время. То есть процессором они выполняются последовательно. Ядро процессора быстро переключается с одного потока на другой, и у пользователя создается впечатление, что программа выполняется одновременно в нескольких местах.

Это не проблема! Ведь когда появились мультизадачные операционные системы (Windows-95, Linux) в мире всё еще господствовали одно-ядерные процессоры (Pentium), и никого не напрягало, что процессор быстро переключается между потоками, создавая ощущение параллельного их выполнения.

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

Если бы нам не хватало быстродействия Питона для работы с последовательным портом, то мы бы его откинули не задумываясь. А поскольку мощности современных компов более чем достаточно, то на первый план выползают не проблемы быстродействия, а проблемы нашего удобства (или оптимальности) для достижения результата.

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

И я вас прошу набраться ещё чуточку терпения! Мне нужно вам поведать о способах создания многопоточных программ в Питоне.

Ещё существуют многопроцессные программы, но это не наш случай. Поэтому просто игнорируем это направление и занимаемся чисто многопоточностью.

Python позволяет создавать многопоточные программы двумя способами. Первый способ заключается в том, чтобы написать некоторую функцию и запустить её как отдельный поток. Второй способ предлагает воспользоваться классом для создания потока. Мы будем писать программу по второму способу.

Для работы с потоками в Python имеется модуль threading. Помимо всего прочего в этом модуле имеется класс Thread, который отвечает за создание и работу потока.

В классе Thread есть много чего, но нам нужно обратить внимание всего не несколько вещей.

Во первых,  для работы с потоками мы должны создавать свои классы на базе этого класса.

Во вторых, в нашем дочернем классе должна быть прописана функция run() — это ничто иное, как функция потока. В этой функции заключается весь функционал того, что должен выполнять наш поток.

В третьих, чтобы стартовать наш поток нудно выполнить функцию (метод) базового класса Thread, которая так и называется — start(). Вызывать функцию run мы не должны, это будет ошибкой.

В четвертых, когда функция run() прекращает свою работу (управление выходит из функции), поток так же прекращает свое существование. Если функция будет основана на бесконечном цикле:

while True:
  do_something()

, то поток никогда не закончит свою работу.

В пятых, чтобы остановить работу потока при завершении работы главного потока, в классе потока нужно установить атрибут daemon в значение True. Иначе при завершении главного потока программа никогда не закончится.

Вот полный текст многопоточной программы:

#!/usr/bin/env python
#coding: utf-8

import threading
import time


################################################################################
'''
Класс дочернего потока.
Дочерний поток должен иметь атрибут self.daemon = True, иначе при завершении
главного потока он не завершиться и программа никогда не прекратит свое
выполнение
'''
class MyThread(threading.Thread):

  '''
  Обычный конструктор
  '''
  def __init__(self):
    threading.Thread.__init__(self)
    self.daemon = True
  
  '''
  Функция потока.
  Эта функция не должна вызываться напрямую из главного потока.
  Запуск функции потока осуществляется вызовом метода start
  '''
  def run(self):
    print "Поехали!"
   
    counter = 0
    while True:
      print counter
      counter += 1
      time.sleep(1)
      
    print "Остановились"


################################################################################
if __name__ == "__main__":

  myth = MyThread()
  myth.start()
  
  while True:
    ans = raw_input("Остановить процесс?")
    if ans == "y":
      print "Стоп!"
      break
  
  print "Конец"

Его можно и нужно брать в качестве основы (шаблона) для создания многопоточных программ.

Программа не делает ничего полезного. Но она позволяет быстро стартовать для создания многопоточных программ.

Что в программе?

Во первых, На базе класса Thread мы создали свой класс MyThread. В этом классе мы написали функцию потока run(), которая при запуске напечатает слово «Поехали!» и будет раз в секунду выдавать значение счетчика count.

Обратите внимание на строку

    self.daemon = True

в конструкторе класса. Устанавливая атрибут daemon в True мы позволяем потоку остановиться при завершении главного потока.

А чем занимается главный поток?

Главный поток программы создает вторичный поток и запускает его. Затем главный поток переходит к циклу опроса клавиатуры. Из этого цикла есть только один выход — нажать клавишу ‘y’ и затем клавишу «Enter».

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

Программа преднамеренно сделана максимально простой. Берите её и играйтесь! Создайте три, пять, десять потоков! Берите и изучайте!

В следующей части мы рассмотрим создание более-менее полезной программы для работы с последовательным портом.

Advertisements

2 responses to “Как приручить свой Bluetooth. Часть 3

  1. Циклы в run() можно останавливать. Например, когда в главный модуль прилетает Ctrl-C, то желательно дать команду всем потокам завершить свою работу. С этим отлично справляется объект семафора, который передаётся в конструктор своего класса потока.

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

      Сначала нужно хоть как-то начать говорить на иностранном языке. Хоть что-нибудь «лопотать». А совершенство будем оттачивать в процессе. Дети ведь, не сразу начинают говорить правильно и красиво, но постепенно научаются в процессе ежедневного использования языка. Так и с написанием программам — программы должны хоть как-то работать, и должен быть гуру, который в процессе развития программы будет корректировать её.

      Спасибо за комментарий!

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s