Interrput a thread

G

gervaz

Hi all, I need to stop a threaded (using CTR+C or kill) application if
it runs too much or if I decide to resume the work later.
I come up with the following test implementation but I wanted some
suggestion from you on how I can implement what I need in a better or
more pythonic way. Here the code:

import os
import signal
import time
from threading import Thread, current_thread
from queue import LifoQueue, Empty

COMMAND = {"STOP": 0, "NORMAL": 1}
THREAD_NUM = 5

lq = LifoQueue()

print("{0}\n".format(os.getpid()))

class InterceptInterrupt(Exception):
pass

class Handler:
def __init__(self, queue):
self._queue = queue
def __del__(self):
print("Bye bye!")
def getHandler(self, signum, frame):
print("Interrupt raised!")
for _ in range(THREAD_NUM):
self._queue.put((COMMAND["STOP"], None))
raise InterceptInterrupt

h = Handler(lq)

signal.signal(signal.SIGINT, h.getHandler)

for i in range(25):
lq.put((COMMAND["NORMAL"], i))

def do_work(queue):
while True:
time.sleep(5)
try:
cmd, value = queue.get(block=False)
if cmd == COMMAND["STOP"]:
print("{0}: STOP command
received!".format(current_thread().name))
break
elif cmd == COMMAND["NORMAL"]:
print(value)
except Empty:
break

threads = [Thread(target=do_work, args=(lq,)) for _ in
range(THREAD_NUM)]

for t in threads:
t.start()

while not lq.empty():
try:
time.sleep(1)
except (IOError, InterceptInterrupt):
break

for t in threads:
t.join()

if lq.empty():
print("The queue is empty.")
else:
print("The queue is NOT empty. Some additional work has to be
done.")

Thank you,
Mattia
 
J

John Nagle

Hi all, I need to stop a threaded (using CTR+C or kill) application if
it runs too much or if I decide to resume the work later.
I come up with the following test implementation but I wanted some
suggestion from you on how I can implement what I need in a better or
more pythonic way.

You can't send signals to individual threads under CPython.
Even where the OS supports it, CPython does not. See
"http://docs.python.org/library/signal.html"

Nor can you kill a thread. All you can do is ask it
nicely to stop itself.

Even worse, sending control-C to a multi-thread program
is unreliable in CPython. See "http://blip.tv/file/2232410"
for why. It's painful.

John Nagle
 
A

Alice Bevan–McGregor

Even worse, sending control-C to a multi-thread program
is unreliable in CPython. See "http://blip.tv/file/2232410"
for why. It's painful.

AFIK, that has been resolved in Python 3.2 with the introduction of an
intelligent thread scheduler as part of the GIL release/acquire process.

- Alice.
 
G

gervaz

AFIK, that has been resolved in Python 3.2 with the introduction of an
intelligent thread scheduler as part of the GIL release/acquire process.

        - Alice.

Ok, but then suppose I have multiple long running threads that I want
to delete/suspend because they are tooking too much time, which
solution do you propose?

Thanks,

Mattia
 
D

Diez B. Roggisch

gervaz said:
Ok, but then suppose I have multiple long running threads that I want
to delete/suspend because they are tooking too much time, which
solution do you propose?

If possible, use multiple processes instead.

Diez
 
G

gervaz

If possible, use multiple processes instead.

Diez- Nascondi testo citato

- Mostra testo citato -

Multiple processes, ok, but then regarding processes' interruption
there will be the same problems pointed out by using threads?

Mattia
 
J

Jean-Paul Calderone

Multiple processes, ok, but then regarding processes' interruption
there will be the same problems pointed out by using threads?

No. Processes can be terminated easily on all major platforms. See
`os.kill`.

Jean-Paul
 
A

Adam Skutt

No.  Processes can be terminated easily on all major platforms.  See
`os.kill`.

Yes, but that's not the whole story, now is it? It's certainly much
more reliable and easier to kill a process. It's not any easier to do
it and retain defined behavior, depending on exactly what you're
doing. For example, if you kill it while it's in the middle of
updating shared memory, you can potentially incur undefined behavior
on the part of any process that can also access shared memory.

In short, taking a program that uses threads and shared state and
simply replacing the threads with processes will likely not gain you a
thing. It entirely depends on what those threads are doing and how
they do it.

Adam
 
G

gervaz

Yes, but that's not the whole story, now is it?  It's certainly much
more reliable and easier to kill a process.  It's not any easier to do
it and retain defined behavior, depending on exactly what you're
doing.  For example, if you kill it while it's in the middle of
updating shared memory, you can potentially incur undefined behavior
on the part of any process that can also access shared memory.

In short, taking a program that uses threads and shared state and
simply replacing the threads with processes will likely not gain you a
thing.  It entirely depends on what those threads are doing and how
they do it.

Adam

As per the py3.1 documentation, os.kill is only available in the Unix
os. Regarding the case pointed out by Adam I think the best way to
deal with it is to create a critical section so that the shared memory
will be updated in an atomic fashion. Btw it would be useful to take a
look at some actual code/documentation in order to understand how
others dealt with the problem...

Ciao,

Mattia
 
J

Jean-Paul Calderone

Yes, but that's not the whole story, now is it?  It's certainly much
more reliable and easier to kill a process.  It's not any easier to do
it and retain defined behavior, depending on exactly what you're
doing.  For example, if you kill it while it's in the middle of
updating shared memory, you can potentially incur undefined behavior
on the part of any process that can also access shared memory.

Then don't use shared memory.
In short, taking a program that uses threads and shared state and
simply replacing the threads with processes will likely not gain you a
thing.  It entirely depends on what those threads are doing and how
they do it.

Of course. The whole point here is not about threads vs processes.
It's about shared memory concurrency vs non-shared memory
concurrency. You can implement both with threads and both with
processes, but threads are geared towards shared memory and processes
are geared towards non-shared memory. So what most people mean by
"use processes" is "don't use shared memory".

Jean-Paul
 
D

Diez B. Roggisch

gervaz said:
As per the py3.1 documentation, os.kill is only available in the Unix
os. Regarding the case pointed out by Adam I think the best way to
deal with it is to create a critical section so that the shared memory
will be updated in an atomic fashion. Btw it would be useful to take a
look at some actual code/documentation in order to understand how
others dealt with the problem...

There is the multiprocessing module. It's a good start, and works
cross-platform.

Diez
 
A

Adam Skutt

Regarding the case pointed out by Adam I think the best way to
deal with it is to create a critical section so that the shared memory
will be updated in an atomic fashion.

Ok, so if the OS kills the process between taking the lock and
releasing it, what are you going to do? Handled naively, you can end
up in a deadlock situation[1]. If a process has locked a semaphore and
then terminates, the semaphore retains its current value: all other
processes waiting on the semaphore will still be waiting, and any new
processes that access the semaphore will wait as well. In short, if a
process holding a semaphore dies, you have to manually unlock it.

For signals that the process intends to handle, you can always disable
them before taking the lock and reenable them after releasing it. Or,
you can install signal handlers in each process that ensure everything
is properly cleaned up when the signal is delivered. Which solution
is right depends on what you're doing.

For signals you don't intend to handle (SIGSEGV) or cannot handle
(SIGKILL) there's not much you can do. It's potentially dangerous to
continue on after a child has received such a signal, so the right
solution may be to literally do nothing and let the deadlock occur.
If you must do cleanup, it must be done carefully. The key thing to
understand is that the problems with killing threads haven't gone
away: delivering the "please die" message isn't the hard part; it's
safely cleaning up the thread in such a way it doesn't break the rest
of the application! This problem still exists if you replace threads
with processes (assuming you're using shared memory).

As such, the better thing to do, if possible, is to avoid shared
memory and use constructs like pipes and sockets for I/O. They have
much better defined failure semantics, and do allow you a modicum of
fault isolation. At the very least, graceful shutdown is much easier.

HTH,
Adam

[1] For SIGINT from the terminal, the "right thing" /might/ happen.
Strong emphasis on the might.
 
A

Adam Skutt

Of course.  The whole point here is not about threads vs processes.
It's about shared memory concurrency vs non-shared memory
concurrency.  You can implement both with threads and both with
processes, but threads are geared towards shared memory and processes
are geared towards non-shared memory.  So what most people mean by
"use processes" is "don't use shared memory".

This is entirely my presumption, but I think if the OP were keenly
aware of the differences between thread and processes, it's pretty
likely he wouldn't have asked his question in the first place.

Also, I've written lots and lots of "use processes" code on multiple
platforms, and much of it has used some sort of shared memory
construct. It's actually pretty common, especially in code bases with
a lot of history. Not all the world is Apache.

Adam
 
J

Jean-Paul Calderone

This is entirely my presumption, but I think if the OP were keenly
aware of the differences between thread and processes, it's pretty
likely he wouldn't have asked his question in the first place.

Fair enough. :)
Also, I've written lots and lots of "use processes" code on multiple
platforms, and much of it has used some sort of shared memory
construct.  It's actually pretty common, especially in code bases with
a lot of history.  Not all the world is Apache.

Hee hee, Apache. :)
 
G

gervaz

Fair enough. :)


Hee hee, Apache. :)




- Mostra testo citato -

BTW thanks for the suggestions. As in my original code snippet, the
shared object that the threads are using is the Queue, that itself
implement locking mechanism so I don't have to worry about concurrent
access. Regarding my question, it was just to have a hint on how a
thread termination can be handled as, per example, you have the
consumer-producer pattern.

Mattia
 
F

Fuzzyman

Hi all, I need to stop a threaded (using CTR+C or kill) application if
it runs too much or if I decide to resume the work later.
I come up with the following test implementation but I wanted some
suggestion from you on how I can implement what I need in a better or
more pythonic way. Here the code:

This is a case that .NET (C#) handles better than Python or Java.

It is unsafe to terminate an os level thread at an arbitrary point
because it may be executing code in a critical section. Both Java
and .NET used to provide ways to terminate threads "safely" by raising
an asynchronous exception in the thread. Releasing locks (etc) that
the thread holds could then be done in a finally section protecting
the code. Python doesn't allow you to abort threads.

Unfortunately the thread abort exception could also be raised in the
finally section - prematurely aborting the lock / resource cleanup.

Java handled this by deprecating thread aborting. (Python has never
had it I believe.)

..NET handled it by changing the semantics of thread aborting - the
thread abort exception will never be raised in a finally block. This
makes thread aborting safe, although technically you can subvert it by
putting all your code in a finally block (you can also catch and
cancel the thread abort exception).

The standard advice is to use a flag and do manual checking to abort
threads. This only works for fine grained operations and *doesn't*
work for very coarse grained operations or where there aren't
convenient places to check the flag. It's another place where people
sometimes have a genuine need/use case yet people will insist on
telling them they don't *really* want it...

Anyway, although there are ways based on ctypes to abort Python
threads it's not really safe. If you google you should find them,
hopefully with intelligible caveat emptor warnings...

All the best,

Michael Foord

import os
import signal
import time
from threading import Thread, current_thread
from queue import LifoQueue, Empty

COMMAND = {"STOP": 0, "NORMAL": 1}
THREAD_NUM = 5

lq = LifoQueue()

print("{0}\n".format(os.getpid()))

class InterceptInterrupt(Exception):
    pass

class Handler:
    def __init__(self, queue):
        self._queue = queue
    def __del__(self):
        print("Bye bye!")
    def getHandler(self, signum, frame):
        print("Interrupt raised!")
        for _ in range(THREAD_NUM):
            self._queue.put((COMMAND["STOP"], None))
        raise InterceptInterrupt

h = Handler(lq)

signal.signal(signal.SIGINT, h.getHandler)

for i in range(25):
    lq.put((COMMAND["NORMAL"], i))

def do_work(queue):
    while True:
        time.sleep(5)
        try:
            cmd, value = queue.get(block=False)
            if cmd == COMMAND["STOP"]:
                print("{0}: STOP command
received!".format(current_thread().name))
                break
            elif cmd == COMMAND["NORMAL"]:
                print(value)
        except Empty:
            break

threads = [Thread(target=do_work, args=(lq,)) for _ in
range(THREAD_NUM)]

for t in threads:
    t.start()

while not lq.empty():
    try:
        time.sleep(1)
    except (IOError, InterceptInterrupt):
        break

for t in threads:
    t.join()

if lq.empty():
    print("The queue is empty.")
else:
    print("The queue is NOT empty. Some additional work has to be
done.")

Thank you,
Mattia
 
F

Fuzzyman

This is a case that .NET (C#) handles better than Python or Java.

Heh, so one possible option is to use IronPython.... :)

Michael Foord
It is unsafe to terminate an os level thread at an arbitrary point
because it may be executing code in a critical section. Both Java
and .NET used to provide ways to terminate threads "safely" by raising
an asynchronous exception in the thread. Releasing locks (etc) that
the thread holds could then be done in a finally section protecting
the code. Python doesn't allow you to abort threads.

Unfortunately the thread abort exception could also be raised in the
finally section - prematurely aborting the lock / resource cleanup.

Java handled this by deprecating thread aborting. (Python has never
had it I believe.)

.NET handled it by changing the semantics of thread aborting - the
thread abort exception will never be raised in a finally block. This
makes thread aborting safe, although technically you can subvert it by
putting all your code in a finally block (you can also catch and
cancel the thread abort exception).

The standard advice is to use a flag and do manual checking to abort
threads. This only works for fine grained operations and *doesn't*
work for very coarse grained operations or where there aren't
convenient places to check the flag. It's another place where people
sometimes have a genuine need/use case yet people will insist on
telling them they don't *really* want it...

Anyway, although there are ways based on ctypes to abort Python
threads it's not really safe. If you google you should find them,
hopefully with intelligible caveat emptor warnings...

All the best,

Michael Foord


import os
import signal
import time
from threading import Thread, current_thread
from queue import LifoQueue, Empty
COMMAND = {"STOP": 0, "NORMAL": 1}
THREAD_NUM = 5
lq = LifoQueue()

class InterceptInterrupt(Exception):
    pass
class Handler:
    def __init__(self, queue):
        self._queue = queue
    def __del__(self):
        print("Bye bye!")
    def getHandler(self, signum, frame):
        print("Interrupt raised!")
        for _ in range(THREAD_NUM):
            self._queue.put((COMMAND["STOP"], None))
        raise InterceptInterrupt
h = Handler(lq)
signal.signal(signal.SIGINT, h.getHandler)
for i in range(25):
    lq.put((COMMAND["NORMAL"], i))
def do_work(queue):
    while True:
        time.sleep(5)
        try:
            cmd, value = queue.get(block=False)
            if cmd == COMMAND["STOP"]:
                print("{0}: STOP command
received!".format(current_thread().name))
                break
            elif cmd == COMMAND["NORMAL"]:
                print(value)
        except Empty:
            break
threads = [Thread(target=do_work, args=(lq,)) for _ in
range(THREAD_NUM)]
for t in threads:
    t.start()
while not lq.empty():
    try:
        time.sleep(1)
    except (IOError, InterceptInterrupt):
        break
for t in threads:
    t.join()
if lq.empty():
    print("The queue is empty.")
else:
    print("The queue is NOT empty. Some additional work has to be
done.")
Thank you,
Mattia
 
R

Roy Smith

Fuzzyman said:
It is unsafe to terminate an os level thread at an arbitrary point
because it may be executing code in a critical section.
[...]
The standard advice is to use a flag and do manual checking to abort
threads. This only works for fine grained operations and *doesn't*
work for very coarse grained operations or where there aren't
convenient places to check the flag.

Another possibility is to not use threads! If you

1) Need asynchronous operation

2) Need iterruptability

3) Can't poll for an "please stop" signal

You should look at running your "thread" as a separate process, which
you can send a kill signal to when you want it to go away. You can then
communicate with it via pipes, sockets, shared memory segments, whatever.

Threads are a wonderful invention, but they are not a panacea for all
possible parallelism tasks. Sometimes they're just the wrong tool.
 
F

Fuzzyman

 Fuzzyman said:
It is unsafe to terminate an os level thread at an arbitrary point
because it may be executing code in a critical section.
[...]
The standard advice is to use a flag and do manual checking to abort
threads. This only works for fine grained operations and *doesn't*
work for very coarse grained operations or where there aren't
convenient places to check the flag.

Another possibility is to not use threads!  If you

1) Need asynchronous operation

2) Need iterruptability

3) Can't poll for an "please stop" signal

You should look at running your "thread" as a separate process, which
you can send a kill signal to when you want it to go away.  You can then
communicate with it via pipes, sockets, shared memory segments, whatever.

Threads are a wonderful invention, but they are not a panacea for all
possible parallelism tasks.  Sometimes they're just the wrong tool.

However some tasks / algorithms are done much better with threads than
processes.

Asynchronous operations are good for IO bound concurrency but not for
CPU bound concurrency.

Michael Foord
 
J

Jean-Paul Calderone

<2ebc11a5-1b45-4faa-97b9-c84f0db01...@k22g2000yqh.googlegroups.com>,
 Fuzzyman said:
It is unsafe to terminate an os level thread at an arbitrary point
because it may be executing code in a critical section.
[...]
The standard advice is to use a flag and do manual checking to abort
threads. This only works for fine grained operations and *doesn't*
work for very coarse grained operations or where there aren't
convenient places to check the flag.
Another possibility is to not use threads!  If you
1) Need asynchronous operation
2) Need iterruptability
3) Can't poll for an "please stop" signal
You should look at running your "thread" as a separate process, which
you can send a kill signal to when you want it to go away.  You can then
communicate with it via pipes, sockets, shared memory segments, whatever.
Threads are a wonderful invention, but they are not a panacea for all
possible parallelism tasks.  Sometimes they're just the wrong tool.

However some tasks / algorithms are done much better with threads than
processes.

Asynchronous operations are good for IO bound concurrency but not for
CPU bound concurrency.

Asynchronous and threads aren't mutually exclusive. An asynchronous
multithreading or multiprocessing library is perfectly well suited for
CPU bound concurrency.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top