[Tutor] glibc error while Python script runs - Solved

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Fri Jan 20 04:23:25 CET 2006



On Thu, 19 Jan 2006, Kent Johnson wrote:


> In your original desing were you sharing a connection between threads?
> That could cause trouble. But if each connection has its own thread and
> you are using transactions and isolation levels appropriately, they
> shouldn't stomp on each other.

Hi Kent and Bernard,

It's possible that MySQLdb was linked against the non-thread-safe
mysqlclient (rather than mysqlclient_r) library; that would probably cause
havoc.


> > So the solution was to start some sort of queue server in a separate
> > thread. This queue would consist of a list, and each time the program
> > would want to perform a MySQL operation, it would add it to the queue.

We may want to use the Queue module here instead of a list; the way the
program is described now sounds like the server is busy-spinning when it
looks for new things to do.  The thing that makes busy-spinning slightly
not-nice is that it consumes CPU regardless if the system's doing anything
or not.

But a more serious problem than busy-waiting is one of thread-safety: the
object that's used to communicate between two threads --- a shared list
--- might be unreliable if the list methods aren't thread-safe.  And I'm
not certain that all the list methods are thread-safe.

That is, the problem with mutual exclusion may have just been pushed up
the program's structure, from the MySQL queries up to the shared queue
list access.  *grin*


The synchronized Queue described in:

    http://www.python.org/doc/lib/module-Queue.html

is designed to be a reliable communication medium between threads, and I
strongly recommend you look at it, especially because you've seen
first-hand the weird things that can happen with threads.  *grin*


Here's a small example that shows what a different Queue can make:

############################################
from threading import Thread

class Server:
    def __init__(self):
        self.queue = []

    def acceptJob(self, query):
        self.queue.append((query,))

    def shutdownOnIdle(self):
        self.queue.append("QUIT!")

    def jobLoop(self):
        while True:
            print "looping"
            if len(self.queue) > 0:
               nextJob = self.queue.pop(0)
               if nextJob == "QUIT!":
                   return
               print "I should do", nextJob

    def startServer(self):
        Thread(target=self.jobLoop).start()

if __name__ == '__main__':
    server = Server()
    server.startServer()
    server.acceptJob("a")
    server.acceptJob("b")
    server.acceptJob("c")
    server.shutdownOnIdle()
############################################


Running this will show just how much work a busy-waiting thread does.


But if we change a few methods to use Queues instead of lists:

######
from threading import Thread
from Queue import Queue

class Server:
    def __init__(self):
        self.queue = Queue()

    def acceptJob(self, query):
        self.queue.put((query,))

    def shutdownOnIdle(self):
        self.queue.put("QUIT!")

    def jobLoop(self):
        while True:
            print "looping"
            nextJob = self.queue.get()
            if nextJob == "QUIT!":
                return
            print "I should do", nextJob

    def startServer(self):
        Thread(target=self.jobLoop).start()

if __name__ == '__main__':
    server = Server()
    server.startServer()
    server.acceptJob("a")
    server.acceptJob("b")
    server.acceptJob("c")
    server.shutdownOnIdle()
######

and compare the output of this to the original implementation, it should
be clearer why Queues are cool.  *grin*


Does this make sense?  I hope this helps!



More information about the Tutor mailing list