Có thể chấm dứt một chuỗi đang chạy mà không cần thiết lập / kiểm tra bất kỳ cờ / semaphores / vv.?
Có thể chấm dứt một chuỗi đang chạy mà không cần thiết lập / kiểm tra bất kỳ cờ / semaphores / vv.?
Câu trả lời:
Nói chung là một mô hình xấu để giết một luồng đột ngột, bằng Python và bằng bất kỳ ngôn ngữ nào. Hãy nghĩ về các trường hợp sau đây:
Cách xử lý tốt này nếu bạn có đủ khả năng (nếu bạn đang quản lý các luồng của riêng mình) là có một cờ exit_Vquest mà mỗi luồng kiểm tra trên khoảng thời gian thông thường để xem liệu đã đến lúc nó thoát ra chưa.
Ví dụ:
import threading
class StoppableThread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""
def __init__(self, *args, **kwargs):
super(StoppableThread, self).__init__(*args, **kwargs)
self._stop_event = threading.Event()
def stop(self):
self._stop_event.set()
def stopped(self):
return self._stop_event.is_set()
Trong mã này, bạn nên gọi stop()
luồng khi bạn muốn nó thoát và đợi luồng thoát đúng bằng cách sử dụng join()
. Các chủ đề nên kiểm tra cờ dừng trong khoảng thời gian thường xuyên.
Có những trường hợp tuy nhiên khi bạn thực sự cần phải giết một chủ đề. Một ví dụ là khi bạn đang gói một thư viện bên ngoài đang bận cho các cuộc gọi dài và bạn muốn làm gián đoạn nó.
Đoạn mã sau cho phép (với một số hạn chế) đưa ra Ngoại lệ trong luồng Python:
def _async_raise(tid, exctype):
'''Raises an exception in the threads with id tid'''
if not inspect.isclass(exctype):
raise TypeError("Only types can be raised (not instances)")
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),
ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
# "if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
raise SystemError("PyThreadState_SetAsyncExc failed")
class ThreadWithExc(threading.Thread):
'''A thread class that supports raising exception in the thread from
another thread.
'''
def _get_my_tid(self):
"""determines this (self's) thread id
CAREFUL : this function is executed in the context of the caller
thread, to get the identity of the thread represented by this
instance.
"""
if not self.isAlive():
raise threading.ThreadError("the thread is not active")
# do we have it cached?
if hasattr(self, "_thread_id"):
return self._thread_id
# no, look for it in the _active dict
for tid, tobj in threading._active.items():
if tobj is self:
self._thread_id = tid
return tid
# TODO: in python 2.6, there's a simpler way to do : self.ident
raise AssertionError("could not determine the thread's id")
def raiseExc(self, exctype):
"""Raises the given exception type in the context of this thread.
If the thread is busy in a system call (time.sleep(),
socket.accept(), ...), the exception is simply ignored.
If you are sure that your exception should terminate the thread,
one way to ensure that it works is:
t = ThreadWithExc( ... )
...
t.raiseExc( SomeException )
while t.isAlive():
time.sleep( 0.1 )
t.raiseExc( SomeException )
If the exception is to be caught by the thread, you need a way to
check that your thread has caught it.
CAREFUL : this function is executed in the context of the
caller thread, to raise an excpetion in the context of the
thread represented by this instance.
"""
_async_raise( self._get_my_tid(), exctype )
(Dựa trên Chủ đề có thể giết được của Tomer Filiba. Câu trích dẫn về giá trị trả về PyThreadState_SetAsyncExc
dường như là từ một phiên bản cũ của Python .)
Như đã lưu ý trong tài liệu, đây không phải là một viên đạn ma thuật bởi vì nếu luồng đang bận bên ngoài trình thông dịch Python, nó sẽ không bị gián đoạn.
Một mô hình sử dụng tốt của mã này là để luồng bắt một ngoại lệ cụ thể và thực hiện việc dọn dẹp. Bằng cách đó, bạn có thể làm gián đoạn một nhiệm vụ và vẫn có dọn dẹp thích hợp.
SO_REUSEADDR
tùy chọn ổ cắm để tránh Address already in use
lỗi.
None
thay vì 0
cho các res != 1
trường hợp, và tôi đã phải gọi ctypes.c_long(tid)
và thông qua đó để bất kỳ ctypes chức năng chứ không phải là tid trực tiếp.
Không có API chính thức để làm điều đó, không.
Bạn cần sử dụng API nền tảng để tiêu diệt luồng, ví dụ pthread_kill hoặc TerminatingThread. Bạn có thể truy cập API như vậy, ví dụ như thông qua pythonwin hoặc thông qua ctypes.
Lưu ý rằng điều này vốn không an toàn. Nó có thể sẽ dẫn đến rác không thể kiểm soát (từ các biến cục bộ của khung stack trở thành rác) và có thể dẫn đến bế tắc, nếu luồng bị giết có GIL tại điểm khi nó bị giết.
Một multiprocessing.Process
lonp.terminate()
Trong trường hợp tôi muốn giết một luồng, nhưng không muốn sử dụng cờ / khóa / tín hiệu / semaphores / sự kiện / bất cứ điều gì, tôi quảng bá các luồng cho các quy trình đầy đủ. Đối với mã sử dụng chỉ một vài luồng, chi phí không phải là xấu.
Ví dụ, điều này có ích để dễ dàng chấm dứt các "luồng" của trình trợ giúp thực thi chặn I / O
Việc chuyển đổi là không đáng kể: Trong mã liên quan thay thế tất cả threading.Thread
bằng multiprocessing.Process
và tất cả queue.Queue
bằng multiprocessing.Queue
và thêm các cuộc gọi cần thiết p.terminate()
vào quy trình cha mẹ của bạn muốn giết con của nóp
multiprocessing
là tốt, nhưng lưu ý rằng các đối số được chọn lọc cho quy trình mới. Vì vậy, nếu một trong các đối số là một cái gì đó không thể chọn (như a logging.log
), nó có thể không phải là một ý tưởng tốt để sử dụng multiprocessing
.
multiprocessing
các đối số được chọn theo quy trình mới trên Windows, nhưng Linux sử dụng tính năng giả mạo để sao chép chúng (Python 3.7, không chắc chắn các phiên bản khác). Vì vậy, bạn sẽ kết thúc với mã hoạt động trên Linux nhưng lại phát sinh lỗi trên Windows.
multiprocessing
với đăng nhập là kinh doanh khó khăn. Cần sử dụng QueueHandler
(xem hướng dẫn này ). Tôi đã học nó một cách khó khăn.
Nếu bạn đang cố gắng chấm dứt toàn bộ chương trình, bạn có thể đặt luồng là "daemon". xem chủ đề.daemon
Như những người khác đã đề cập, tiêu chuẩn là đặt cờ dừng. Đối với một cái gì đó nhẹ (không phân lớp của Thread, không có biến toàn cục), một cuộc gọi lại lambda là một tùy chọn. (Lưu ý dấu ngoặc đơn trong if stop()
.)
import threading
import time
def do_work(id, stop):
print("I am thread", id)
while True:
print("I am thread {} doing something".format(id))
if stop():
print(" Exiting loop.")
break
print("Thread {}, signing off".format(id))
def main():
stop_threads = False
workers = []
for id in range(0,3):
tmp = threading.Thread(target=do_work, args=(id, lambda: stop_threads))
workers.append(tmp)
tmp.start()
time.sleep(3)
print('main: done sleeping; time to stop the threads.')
stop_threads = True
for worker in workers:
worker.join()
print('Finis.')
if __name__ == '__main__':
main()
Thay thế print()
bằng một pr()
hàm luôn tuôn ra ( sys.stdout.flush()
) có thể cải thiện độ chính xác của đầu ra vỏ.
(Chỉ được thử nghiệm trên Windows / Eclipse / Python3.3)
pr()
Chức năng là gì?
Điều này dựa trên thread2 - chủ đề có thể giết được (công thức Python)
Bạn cần gọi PyThreadState_SetasyncExc (), chỉ có sẵn thông qua ctypes.
Điều này chỉ được thử nghiệm trên Python 2.7.3, nhưng nó có khả năng hoạt động với các bản phát hành 2.x khác gần đây.
import ctypes
def terminate_thread(thread):
"""Terminates a python thread from another thread.
:param thread: a threading.Thread instance
"""
if not thread.isAlive():
return
exc = ctypes.py_object(SystemExit)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
ctypes.c_long(thread.ident), exc)
if res == 0:
raise ValueError("nonexistent thread id")
elif res > 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
raise SystemError("PyThreadState_SetAsyncExc failed")
KeyboardInterrupt
để họ có cơ hội dọn dẹp. Nếu họ VẪN treo sau đó, thì SystemExit
phù hợp hoặc chỉ giết quá trình từ thiết bị đầu cuối.
pthread_cleanup_push()/_pop()
, sẽ có rất nhiều công việc để thực hiện chính xác và nó sẽ làm chậm trình thông dịch một cách đáng chú ý.
Bạn không bao giờ nên giết một chủ đề mà không hợp tác với nó.
Giết một luồng sẽ loại bỏ bất kỳ đảm bảo nào mà các khối thử / cuối cùng được thiết lập để bạn có thể khóa các khóa, mở tệp, v.v.
Lần duy nhất bạn có thể lập luận rằng việc giết các chủ đề cưỡng bức là một ý tưởng tốt là giết một chương trình nhanh, nhưng không bao giờ là các chủ đề đơn lẻ.
Trong Python, bạn chỉ đơn giản là không thể giết một Thread trực tiếp.
Nếu bạn KHÔNG thực sự cần phải có Thread (!), Những gì bạn có thể làm, thay vì sử dụng gói luồng , là sử dụng gói đa xử lý . Ở đây, để giết một tiến trình, bạn chỉ cần gọi phương thức:
yourProcess.terminate() # kill the process!
Python sẽ giết tiến trình của bạn (trên Unix thông qua tín hiệu SIGTERM, trong khi trên Windows thông qua TerminateProcess()
cuộc gọi). Hãy chú ý sử dụng nó trong khi sử dụng Hàng đợi hoặc Ống! (nó có thể làm hỏng dữ liệu trong Hàng đợi / Ống)
Lưu ý rằng multiprocessing.Event
và multiprocessing.Semaphore
công việc chính xác theo cùng một cách threading.Event
và threading.Semaphore
tương ứng. Trong thực tế, những cái đầu tiên là bản sao của latters.
Nếu bạn thực sự cần sử dụng một Thread, không có cách nào để giết nó trực tiếp. Tuy nhiên, những gì bạn có thể làm là sử dụng một "chủ đề daemon" . Thực tế, trong Python, một Thread có thể được gắn cờ là daemon :
yourThread.daemon = True # set the Thread as a "daemon thread"
Chương trình chính sẽ thoát khi không còn chủ đề không phải daemon còn sống. Nói cách khác, khi luồng chính của bạn (dĩ nhiên là luồng không phải daemon) sẽ hoàn thành các hoạt động của nó, chương trình sẽ thoát ngay cả khi vẫn còn một số luồng daemon hoạt động.
Lưu ý rằng cần phải thiết lập một Chủ đề như daemon
trước khi start()
phương thức được gọi!
Tất nhiên bạn có thể, và nên, sử dụng daemon
ngay cả với multiprocessing
. Ở đây, khi tiến trình chính thoát ra, nó cố gắng chấm dứt tất cả các tiến trình con daemon của nó.
Cuối cùng, xin vui lòng, lưu ý rằng sys.exit()
và os.kill()
không phải là sự lựa chọn.
Bạn có thể giết một luồng bằng cách cài đặt dấu vết vào luồng sẽ thoát khỏi luồng. Xem liên kết đính kèm để thực hiện có thể.
Nếu bạn đang gọi một cách rõ ràng time.sleep()
như một phần của chủ đề của bạn (chẳng hạn bỏ phiếu một số dịch vụ bên ngoài), một sự cải tiến theo phương pháp Phillipe là sử dụng thời gian chờ trong event
's wait()
phương pháp bất cứ nơi nào bạnsleep()
Ví dụ:
import threading
class KillableThread(threading.Thread):
def __init__(self, sleep_interval=1):
super().__init__()
self._kill = threading.Event()
self._interval = sleep_interval
def run(self):
while True:
print("Do Something")
# If no kill signal is set, sleep for the interval,
# If kill signal comes in while sleeping, immediately
# wake up and handle
is_killed = self._kill.wait(self._interval)
if is_killed:
break
print("Killing Thread")
def kill(self):
self._kill.set()
Sau đó để chạy nó
t = KillableThread(sleep_interval=5)
t.start()
# Every 5 seconds it prints:
#: Do Something
t.kill()
#: Killing Thread
Ưu điểm của việc sử dụng wait()
thay vì sleep()
ing và thường xuyên kiểm tra sự kiện là bạn có thể lập trình trong khoảng thời gian ngủ dài hơn, luồng bị dừng gần như ngay lập tức (khi bạn muốn sử dụng sleep()
) và theo tôi, mã để xử lý thoát đơn giản hơn đáng kể .
Hoàn toàn có thể thực hiện một Thread.stop
phương thức như trong mã ví dụ sau:
import sys
import threading
import time
class StopThread(StopIteration):
pass
threading.SystemExit = SystemExit, StopThread
class Thread2(threading.Thread):
def stop(self):
self.__stop = True
def _bootstrap(self):
if threading._trace_hook is not None:
raise ValueError('Cannot run thread with tracing!')
self.__stop = False
sys.settrace(self.__trace)
super()._bootstrap()
def __trace(self, frame, event, arg):
if self.__stop:
raise StopThread()
return self.__trace
class Thread3(threading.Thread):
def _bootstrap(self, stop_thread=False):
def stop():
nonlocal stop_thread
stop_thread = True
self.stop = stop
def tracer(*_):
if stop_thread:
raise StopThread()
return tracer
sys.settrace(tracer)
super()._bootstrap()
###############################################################################
def main():
test1 = Thread2(target=printer)
test1.start()
time.sleep(1)
test1.stop()
test1.join()
test2 = Thread2(target=speed_test)
test2.start()
time.sleep(1)
test2.stop()
test2.join()
test3 = Thread3(target=speed_test)
test3.start()
time.sleep(1)
test3.stop()
test3.join()
def printer():
while True:
print(time.time() % 1)
time.sleep(0.1)
def speed_test(count=0):
try:
while True:
count += 1
except StopThread:
print('Count =', count)
if __name__ == '__main__':
main()
Các Thread3
lớp học dường như chạy mã xấp xỉ 33% nhanh hơn so với Thread2
lớp.
self.__stop
được đặt vào luồng. Lưu ý rằng giống như hầu hết các giải pháp khác ở đây, nó sẽ không thực sự làm gián đoạn cuộc gọi chặn, vì chức năng theo dõi chỉ được gọi khi phạm vi cục bộ mới được nhập. Cũng đáng chú ý là điều đó sys.settrace
thực sự có nghĩa là để triển khai trình gỡ lỗi, hồ sơ, v.v. và như vậy được coi là một chi tiết triển khai của CPython và không được đảm bảo tồn tại trong các triển khai Python khác.
Thread2
lớp là nó chạy mã chậm hơn khoảng mười lần. Một số người vẫn có thể thấy điều này chấp nhận được.
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))
t là Thread
đối tượng của bạn .
Đọc nguồn python ( Modules/threadmodule.c
và Python/thread_pthread.h
) bạn có thể thấy đây Thread.ident
là một pthread_t
loại, vì vậy bạn có thể làm bất cứ điều gì pthread
có thể làm trong việc sử dụng python libpthread
.
Cách giải quyết sau có thể được sử dụng để giết một chủ đề:
kill_threads = False
def doSomething():
global kill_threads
while True:
if kill_threads:
thread.exit()
......
......
thread.start_new_thread(doSomething, ())
Điều này có thể được sử dụng ngay cả để kết thúc các luồng, có mã được viết trong một mô-đun khác, từ luồng chính. Chúng ta có thể khai báo một biến toàn cục trong mô-đun đó và sử dụng nó để chấm dứt luồng / s sinh ra trong mô-đun đó.
Tôi thường sử dụng điều này để chấm dứt tất cả các chủ đề tại lối ra chương trình. Đây có thể không phải là cách hoàn hảo để chấm dứt chủ đề / s nhưng có thể giúp ích.
Tôi đến muộn với trò chơi này, nhưng tôi đã vật lộn với một câu hỏi tương tự và những điều sau đây dường như vừa giải quyết vấn đề một cách hoàn hảo cho tôi VÀ cho phép tôi thực hiện một số kiểm tra và dọn dẹp trạng thái chủ đề cơ bản khi thoát khỏi tiểu trình được tạo ra:
import threading
import time
import atexit
def do_work():
i = 0
@atexit.register
def goodbye():
print ("'CLEANLY' kill sub-thread with value: %s [THREAD: %s]" %
(i, threading.currentThread().ident))
while True:
print i
i += 1
time.sleep(1)
t = threading.Thread(target=do_work)
t.daemon = True
t.start()
def after_timeout():
print "KILL MAIN THREAD: %s" % threading.currentThread().ident
raise SystemExit
threading.Timer(2, after_timeout).start()
Sản lượng:
0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]
SystemExit
trên after_timeout
luồng làm bất cứ điều gì đến luồng chính (chỉ đơn giản là chờ đợi trên luồng trước để thoát trong ví dụ này)?
SystemExit
chỉ có hai tính chất đặc biệt: nó không tạo ra một traceback (khi nào thoát chủ đề bằng cách ném một), và nếu chính lối ra chủ đề bằng cách ném một nó đặt trạng thái thoát (trong khi chờ đợi dù sao đề phi daemon khác để thoát).
Một điều tôi muốn thêm là nếu bạn đọc tài liệu chính thức trong việc phân luồng lib Python , bạn nên tránh sử dụng các luồng "quỷ", khi bạn không muốn các luồng kết thúc đột ngột, với cờ mà Paolo Rovelli đã đề cập .
Từ tài liệu chính thức:
Chủ đề Daemon đột ngột dừng lại khi tắt máy. Tài nguyên của họ (như tệp mở, giao dịch cơ sở dữ liệu, v.v.) có thể không được phát hành đúng cách. Nếu bạn muốn các chủ đề của mình dừng lại một cách duyên dáng, hãy làm cho chúng không phải là daemon và sử dụng một cơ chế báo hiệu phù hợp như Sự kiện.
Tôi nghĩ rằng việc tạo các luồng daemon phụ thuộc vào ứng dụng của bạn, nhưng nói chung (và theo ý kiến của tôi), tốt hơn hết là tránh giết chúng hoặc biến chúng thành daemon. Trong đa xử lý, bạn có thể sử dụng is_alive()
để kiểm tra trạng thái quá trình và "chấm dứt" để hoàn thành chúng (Ngoài ra, bạn tránh được các vấn đề về GIL). Nhưng đôi khi bạn có thể tìm thấy nhiều vấn đề hơn khi bạn thực thi mã của mình trong Windows.
Và luôn nhớ rằng nếu bạn có "luồng trực tiếp", trình thông dịch Python sẽ chạy để chờ chúng. (Vì daemonic này có thể giúp bạn nếu vấn đề không kết thúc đột ngột).
Có một thư viện được xây dựng cho mục đích này, dừng lại . Mặc dù một số cảnh báo tương tự được liệt kê ở đây vẫn được áp dụng, ít nhất thư viện này trình bày một kỹ thuật thường xuyên, có thể lặp lại để đạt được mục tiêu đã nêu.
Mặc dù nó khá cũ, đây có thể là một giải pháp hữu ích cho một số người:
Một mô-đun nhỏ mở rộng chức năng mô-đun của luồng - cho phép một luồng phát sinh ngoại lệ trong ngữ cảnh của luồng khác. Bằng cách nâng cao
SystemExit
, cuối cùng bạn có thể giết chủ đề trăn.
import threading
import ctypes
def _async_raise(tid, excobj):
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(excobj))
if res == 0:
raise ValueError("nonexistent thread id")
elif res > 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
raise SystemError("PyThreadState_SetAsyncExc failed")
class Thread(threading.Thread):
def raise_exc(self, excobj):
assert self.isAlive(), "thread must be started"
for tid, tobj in threading._active.items():
if tobj is self:
_async_raise(tid, excobj)
return
# the thread was alive when we entered the loop, but was not found
# in the dict, hence it must have been already terminated. should we raise
# an exception here? silently ignore?
def terminate(self):
# must raise the SystemExit type, instead of a SystemExit() instance
# due to a bug in PyThreadState_SetAsyncExc
self.raise_exc(SystemExit)
Vì vậy, nó cho phép "luồng xử lý ngoại lệ trong ngữ cảnh của luồng khác" và theo cách này, luồng kết thúc có thể xử lý kết thúc mà không cần thường xuyên kiểm tra cờ hủy bỏ.
Tuy nhiên, theo nguồn gốc của nó , có một số vấn đề với mã này.
- Ngoại lệ sẽ chỉ được nêu ra khi thực thi mã python. Nếu luồng của bạn gọi một hàm chặn gốc / tích hợp, ngoại lệ sẽ chỉ được nêu ra khi thực thi trả về mã python.
- Cũng có một vấn đề nếu hàm tích hợp bên trong gọi PyErr_Clear (), sẽ loại bỏ hiệu quả ngoại lệ đang chờ xử lý của bạn. Bạn có thể cố gắng nâng nó lên một lần nữa.
- Chỉ các loại ngoại lệ có thể được nâng lên một cách an toàn. Các trường hợp ngoại lệ có khả năng gây ra hành vi không mong muốn và do đó bị hạn chế.
- Ví dụ: t1.raise_exc (TypeError) chứ không phải t1.raise_exc (TypeError ("blah")).
- IMHO đó là một lỗi và tôi đã báo cáo nó là một lỗi. Để biết thêm thông tin, http://mail.python.org/pipermail/python-dev/2006-August/068158.html
- Tôi đã yêu cầu hiển thị chức năng này trong mô-đun luồng tích hợp, nhưng vì ctypes đã trở thành một thư viện chuẩn (kể từ 2.5), và
tính năng này không có khả năng là không thể thực hiện, nên nó có thể không được tiếp
xúc.
Pieter Hintjens - một trong những người sáng lập dự án ØMQ - cho biết, sử dụng ØMQ và tránh các nguyên thủy đồng bộ hóa như khóa, mutexes, sự kiện, v.v., là cách an toàn và bảo mật nhất để viết các chương trình đa luồng:
http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ
Điều này bao gồm nói với một chủ đề con, rằng nó sẽ hủy công việc của nó. Điều này sẽ được thực hiện bằng cách trang bị luồng với ổ cắm ØMQ và bỏ phiếu trên ổ cắm đó cho một thông báo nói rằng nó sẽ hủy.
Liên kết cũng cung cấp một ví dụ về mã python đa luồng với ØMQ.
Giả sử, bạn muốn có nhiều luồng của cùng một chức năng, đây là IMHO cách thực hiện dễ nhất để dừng từng luồng theo id:
import time
from threading import Thread
def doit(id=0):
doit.stop=0
print("start id:%d"%id)
while 1:
time.sleep(1)
print(".")
if doit.stop==id:
doit.stop=0
break
print("end thread %d"%id)
t5=Thread(target=doit, args=(5,))
t6=Thread(target=doit, args=(6,))
t5.start() ; t6.start()
time.sleep(2)
doit.stop =5 #kill t5
time.sleep(2)
doit.stop =6 #kill t6
Điều tuyệt vời là ở đây, bạn có thể có nhiều chức năng giống nhau và khác nhau, và ngăn chặn tất cả chúng bằng cách functionname.stop
Nếu bạn muốn chỉ có một luồng của hàm thì bạn không cần phải nhớ id. Chỉ cần dừng lại, nếu doit.stop
> 0.
Chỉ cần xây dựng ý tưởng của @ SCB (chính xác là những gì tôi cần) để tạo một lớp con KillableThread với chức năng tùy chỉnh:
from threading import Thread, Event
class KillableThread(Thread):
def __init__(self, sleep_interval=1, target=None, name=None, args=(), kwargs={}):
super().__init__(None, target, name, args, kwargs)
self._kill = Event()
self._interval = sleep_interval
print(self._target)
def run(self):
while True:
# Call custom function with arguments
self._target(*self._args)
# If no kill signal is set, sleep for the interval,
# If kill signal comes in while sleeping, immediately
# wake up and handle
is_killed = self._kill.wait(self._interval)
if is_killed:
break
print("Killing Thread")
def kill(self):
self._kill.set()
if __name__ == '__main__':
def print_msg(msg):
print(msg)
t = KillableThread(10, print_msg, args=("hello world"))
t.start()
time.sleep(6)
print("About to kill thread")
t.kill()
Đương nhiên, giống như với @SBC, luồng không chờ chạy vòng lặp mới để dừng. Trong ví dụ này, bạn sẽ thấy thông báo "Killing Thread" được in ngay sau "About to kill thread" thay vì đợi thêm 4 giây nữa để luồng hoàn thành (vì chúng tôi đã ngủ được 6 giây rồi).
Đối số thứ hai trong hàm tạo KillableThread là hàm tùy chỉnh của bạn (print_msg tại đây). Đối số đối số là đối số sẽ được sử dụng khi gọi hàm (("hello world")) tại đây.
Như đã đề cập trong câu trả lời của @ Kozyarchuk , cài đặt các công trình theo dõi. Vì câu trả lời này không chứa mã, nên đây là một ví dụ sẵn sàng sử dụng:
import sys, threading, time
class TraceThread(threading.Thread):
def __init__(self, *args, **keywords):
threading.Thread.__init__(self, *args, **keywords)
self.killed = False
def start(self):
self._run = self.run
self.run = self.settrace_and_run
threading.Thread.start(self)
def settrace_and_run(self):
sys.settrace(self.globaltrace)
self._run()
def globaltrace(self, frame, event, arg):
return self.localtrace if event == 'call' else None
def localtrace(self, frame, event, arg):
if self.killed and event == 'line':
raise SystemExit()
return self.localtrace
def f():
while True:
print('1')
time.sleep(2)
print('2')
time.sleep(2)
print('3')
time.sleep(2)
t = TraceThread(target=f)
t.start()
time.sleep(2.5)
t.killed = True
Nó dừng lại sau khi đã in 1
và 2
. 3
không được in.
Bạn có thể thực thi lệnh của mình trong một tiến trình và sau đó giết nó bằng id tiến trình. Tôi cần phải đồng bộ giữa hai luồng mà một trong số đó không tự trả về.
processIds = []
def executeRecord(command):
print(command)
process = subprocess.Popen(command, stdout=subprocess.PIPE)
processIds.append(process.pid)
print(processIds[0])
#Command that doesn't return by itself
process.stdout.read().decode("utf-8")
return;
def recordThread(command, timeOut):
thread = Thread(target=executeRecord, args=(command,))
thread.start()
thread.join(timeOut)
os.kill(processIds.pop(), signal.SIGINT)
return;
Bắt đầu chuỗi con với setDaemon (True).
def bootstrap(_filename):
mb = ModelBootstrap(filename=_filename) # Has many Daemon threads. All get stopped automatically when main thread is stopped.
t = threading.Thread(target=bootstrap,args=('models.conf',))
t.setDaemon(False)
while True:
t.start()
time.sleep(10) # I am just allowing the sub-thread to run for 10 sec. You can listen on an event to stop execution.
print('Thread stopped')
break
Đây là một câu trả lời xấu, xem các ý kiến
Đây là cách thực hiện:
from threading import *
...
for thread in enumerate():
if thread.isAlive():
try:
thread._Thread__stop()
except:
print(str(thread.getName()) + ' could not be terminated'))
Cho nó một vài giây sau đó chủ đề của bạn sẽ được dừng lại. Kiểm tra cũng thread._Thread__delete()
phương pháp.
Tôi muốn giới thiệu một thread.quit()
phương pháp cho thuận tiện. Ví dụ: nếu bạn có một ổ cắm trong luồng của mình, tôi khuyên bạn nên tạo một quit()
phương thức trong lớp xử lý ổ cắm, chấm dứt ổ cắm, sau đó chạy một thread._Thread__stop()
bên trong của bạn quit()
.
_Thread__stop()
chỉ đánh dấu một chủ đề là dừng lại , nó không thực sự dừng chủ đề! Không bao giờ làm điều này. Có một đọc .
Nếu bạn thực sự cần khả năng tiêu diệt một tác vụ phụ, hãy sử dụng một triển khai thay thế. multiprocessing
và gevent
cả hai đều hỗ trợ giết bừa bãi một "sợi chỉ".
Luồng của Python không hỗ trợ hủy bỏ. Thậm chí không thử. Mã của bạn rất có khả năng bị bế tắc, hỏng hoặc rò rỉ bộ nhớ hoặc có các hiệu ứng khó gỡ lỗi "thú vị" khác ngoài ý muốn hiếm khi xảy ra và không đặc biệt.