Monday, August 19, 2019

Thread programming in Python

A simple thread involves doing a task continuously in the background, without any interference on main thread. This could be as simple as to display a running clock or check a DB for updates etc

#!/usr/local/bin/python3

import threading
import datetime
import time

def get_current_time():
    now = datetime.datetime.now()
    return now.strftime("%H:%M:%S")

def print_current_time():
    print(get_current_time())

def print_time_until_interrupt(interval):
    while(True):
        print_current_time()
        time.sleep(1)


if __name__ == "__main__":
    t = threading.Thread(target=print_time_until_interrupt, args=(1,))
    # print(get_current_time())
    t.start();
    t.join();

Change first line #! python path to your local settings, before you execute your code. You see code is both light weight and very easy to follow.

If you'd like to have a class based thread operations it's pretty much the same

import threading
from threading import Thread
import datetime
import time

class MyTimer(Thread):
    def __init__(self, interval):
        Thread.__init__(self)
        self.interval = interval
        
        
    def get_current_time(self):
        now = datetime.datetime.now()
        return now.strftime("%H:%M:%S")

    
    def print_current_time(self):
        print(self.get_current_time())

    
    def run(self):
        while(True):
            self.print_current_time()
            time.sleep(self.interval)


if __name__ == "__main__":
    t = MyTimer(1)
    t.start()
    t.join()

We have a couple of situations to work on threads

  1. Producer consumer
  2. Share data/update between threads
Lets start by looking at Producer consumer scenario. In this case, producer would create an event and consumer will wait until the event is set. If and when set, consumer does his job and clears the evnt and pass the buck back to producer for his process. This continues forever.


#! /usr/local/bin/python3
import time
from threading import Thread, Event
import random


class Consumer(Thread):
    def __init__(self, items, event):
        Thread.__init__(self)
        self.items = items
        self.event = event
    
    def run(self):
        while True:
            time.sleep(2)
            self.event.wait()
            item = self.items.pop()
            print('Consumer notify {0} popped from list by {1}'.format(item, self.name))


class Producer(Thread):
    def __init__(self, items, event):
        Thread.__init__(self)
        self.items = items
        self.event = event

    
    def run(self):
        for i in range(100):
            time.sleep(2)
            item = random.randint(0, 256)
            self.items.append(item)
            print('Producer notify. Added {0} to items in {1}'.format(item, self.name))
            self.event.set()
            print('Producer notify. Event set.')
            self.event.clear()
            print('Producer notify. Event cleared.')

if __name__ == '__main__':
    items = []
    event = Event()
    t1 = Producer(items, event)
    t2 = Consumer(items, event)
    t1.start()
    t2.start()
    t1.join()
    t2.join()




Sharing data between threads can be done in a variety of ways. Easiest is to use Queue. Create a simple Queue (python) data structure and exchange data between them.


#! /usr/local/bin/python3

import time
import random
from threading import Thread, Event
from queue import Queue

class Producer(Thread):
    def __init__(self, queue):
        Thread.__init__(self)
        self.queue = queue

    def run(self):
        for i in range(256):
            item = random.randint(0, 256)
            self.queue.put(item)
            print('Producer {0} - Item {1} added'.format(self.name, item))
            time.sleep(2)

class Consumer(Thread):
    def __init__(self, queue):
        Thread.__init__(self)
        self.queue = queue

    def run(self):
        while(True):
            item = self.queue.get()
            print('Consumer {0} - Popped {1} item from queue'.format(self.name, item))
            self.queue.task_done()

if __name__ == "__main__":
    queue = Queue()
    t1 = Producer(queue)
    t2 = Consumer(queue)
    t3 = Consumer(queue)
    t1.start()
    t2.start()
    t3.start()
    t1.join()
    t2.join()
    t3.join()



Next up Process programming, coming soon....

No comments: