Learning Python Network Programming

(Sean Pound) #1
Chapter 8

A Lock is either locked or unlocked. A thread can lock a thread by either calling
acquire() on it, or as in our program, using it as a context manager. If a thread has
acquired a lock and another thread also tries to acquire the lock, then the second
thread will block on the acquire() call until the first thread releases the lock or exits
the context. There is no limit on the number of threads that can try to acquire a lock
at once – all but the first will block. By wrapping all the accesses to a non-thread safe
object with a lock, we can ensure that no two threads operate on the object at the
same time.


So, every time we add or remove something from send_queues, we wrap it in a
Lock context. Notice that we're also protecting send_queues when we iterate over
it. Even though we're not changing it, we want to be sure that it doesn't get modified
while we're working with it.


Although we're being careful and using locks and thread safe primitives, we're not
protected against all possible thread related pitfalls. Since the thread synchronization
mechanisms themselves block, it's still quite possible to create deadlocks, where
two threads are simultaneously blocking on objects locked by the other thread. The
best approach to managing thread communication is to keep all the accesses to your
shared state restricted to as small an area of your code as you can. In the case of this
server, this module could be reworked as a class providing a minimum number of
public methods. It could also be documented such that it discourages the changing
of any internal state. This will keep this chunk of threading strictly confined to
this class.


A multithreaded chat client


Now that we have a new, all receiving and broadcasting chat server, we just need
a client to go with it. We have mentioned before that we will hit a problem with
our procedural client when trying to listen for both network data and user input
at the same time. Well, now that we have some idea of how to employ threads, we
can have a go at addressing this. Create a new text file called 2.2-chat_client-
multithread.py and save the following code in it:


import sys, socket, threading
import tincanchat

HOST = sys.argv[-1] if len(sys.argv) > 1 else '127.0.0.1'
PORT = tincanchat.PORT

def handle_input(sock):
""" Prompt user for message and send it to server """
print("Type messages, enter to send. 'q' to quit")
Free download pdf