I want to release the GIL

Carl Banks pavlovevidence at gmail.com
Tue Oct 21 05:12:27 EDT 2008


On Oct 21, 1:12 am, Piotr Sobolewski <piotr_sobolew... at o2.pl> wrote:
> Hello,
> I have such program:
>
> import time
> import thread
> def f():
>     global lock
>     while True:
>         lock.acquire()
>         print thread.get_ident()
>         time.sleep(1)
>         lock.release()
> lock=thread.allocate_lock()
> thread.start_new_thread(f,())
> thread.start_new_thread(f,())
> time.sleep(60)

Let me state first of all that you are going about it all wrong.
Simple locks don't handle the above siutation very well because,
frankly, there's no benefit to using threads in this way at all.
Since neither thread can run at the same time as the other, you might
as well have just used ordinary looping.

Normally, when using threads, you only want to use locking for brief
periods when accessing data shared between threads, like so:

def f():
    do_some_stuff
    lock.acquire()
    access_shared_data
    lock.release()
    do_some_more_stuff

Starvation is much less likely to occur in this situation.  (The
interpreter periodically releases the GIL, so if there's stuff between
a release and the next acquire, it'll have a window in which to
release the GIL--unlike in your example where the window was very
tiny.)



> As you can see, I start two threads. Each one works in an infinite
> loop.
> Inside that loop it acquires lock, prints its own id, sleeps a bit and
> then
> releases lock.
>
> When I run it, I notice that only one thread works and the other one
> never
> has a chance to run. I guess it is because the thread don't have a
> chance
> to release the GIL - after it releases the lock, it almost immediately
> (in
> the very next bytecode command) reacquires it.

Not really the GIL's fault.  Or rather, fiddling with the GIL is the
wrong way to get what you want here.

What you want is some kind of thread balancing, to make sure all
threads have a chance to run.  Let me stress that, in this case, you
only need balancing because you're using threads incorrectly.  But
I'll mention one way to do it anyway.

The easiest way I can think of is to use a counter that is protected
by a threading.Condition.  One thread only runs when the counter is
even, the other only when it's odd.  It might be implemented like
this:


counter = 0
c = threading.Condition()

def f():
    global counter
    c.acquire()
    while counter % 1 == 0:  # == 1 for the other thread
        c.wait()
    do_something()
    counter += 1
    c.notify()
    c.release()


The reason threading.Condition is required and not a simple lock is
that simply acquiring the lock is not enough; the counter must be in
the right state as well.


Carl Banks



More information about the Python-list mailing list