2013-02-22 24 views
8

Tôi có timeout trình quản lý ngữ cảnh hoạt động hoàn hảo với tín hiệu nhưng nó làm tăng lỗi ở chế độ đa luồng vì tín hiệu chỉ hoạt động trong chuỗi chính.Trình quản lý ngữ cảnh thời gian Python với chủ đề

def timeout_handler(signum, frame): 
    raise TimeoutException() 

@contextmanager 
def timeout(seconds): 
    old_handler = signal.signal(signal.SIGALRM, timeout_handler) 
    signal.alarm(seconds) 
    try: 
     yield 
    finally: 
     signal.alarm(0) 
     signal.signal(signal.SIGALRM, old_handler) 

Tôi đã nhìn thấy thi trang trí của timeout nhưng tôi không biết làm thế nào để vượt qua yield bên trong lớp có nguồn gốc từ threading.Thread. Biến thể của tôi sẽ không hoạt động.

@contextmanager 
def timelimit(seconds): 
    class FuncThread(threading.Thread): 
     def run(self): 
      yield 

    it = FuncThread()   
    it.start() 
    it.join(seconds) 

    if it.isAlive(): 
     raise TimeoutException() 

Trả lời

-2

Hết giờ cho cuộc gọi hệ thống được thực hiện bằng tín hiệu. Hầu hết các cuộc gọi hệ thống chặn trở lại với EINTR khi tín hiệu xảy ra, vì vậy bạn có thể sử dụng báo thức để thực hiện hết thời gian chờ.

Đây là trình quản lý ngữ cảnh hoạt động với hầu hết các cuộc gọi hệ thống, khiến IOError được nâng lên từ cuộc gọi hệ thống chặn nếu quá trình này mất quá nhiều thời gian.

import signal, errno 
from contextlib import contextmanager 
import fcntl 

@contextmanager 
def timeout(seconds): 
    def timeout_handler(signum, frame): 
     pass 

    original_handler = signal.signal(signal.SIGALRM, timeout_handler) 

    try: 
     signal.alarm(seconds) 
     yield 
    finally: 
     signal.alarm(0) 
     signal.signal(signal.SIGALRM, original_handler) 

with timeout(1): 
    f = open("test.lck", "w") 
    try: 
     fcntl.flock(f.fileno(), fcntl.LOCK_EX) 
    except IOError, e: 
     if e.errno != errno.EINTR: 
      raise e 
     print "Lock timed out" 
+0

Như trong phiên bản 1 của tôi, tôi đã 'ValueError: tín hiệu chỉ hoạt động trong thread' chính trong dòng 'original_handler = signal.signal (signal.SIGALRM, timeout_handler)' – San4ez

+1

Như OP đã nêu, tín hiệu * chỉ hoạt động trong chuỗi chính *. OP cần một giải pháp khác thay thế. –

3

Tôi không thể thấy cách làm những gì bạn đang đề xuất với người quản lý ngữ cảnh, bạn không thể yield luồng từ luồng này sang chuỗi khác. Những gì tôi sẽ làm là bọc chức năng của bạn với một sợi có thể thay đổi được với thời gian chờ. Dưới đây là recipe cho điều đó.

Bạn sẽ có một chuỗi phụ và cú pháp sẽ không đẹp nhưng nó sẽ hoạt động.

+2

Lưu ý rằng chuỗi gián đoạn được mô tả bởi công thức không thực sự bị gián đoạn và thực sự tiếp tục chạy. AFAIK không có cách đáng tin cậy để làm gián đoạn một chuỗi python không chính. – Lethargy

9

Nếu mã được quản lý bởi trình quản lý ngữ cảnh dựa trên vòng lặp, hãy xem xét xử lý cách thức mọi người xử lý việc xóa chuỗi. Giết một luồng khác nói chung là không an toàn, vì vậy cách tiếp cận tiêu chuẩn là để có chuỗi điều khiển thiết lập một cờ hiển thị cho chuỗi công nhân. Chuỗi công nhân định kỳ kiểm tra cờ đó và tự tắt nó đi. Đây là cách bạn có thể làm điều gì đó tương tự với timeout:

class timeout(object): 
    def __init__(self, seconds): 
     self.seconds = seconds 
    def __enter__(self): 
     self.die_after = time.time() + self.seconds 
     return self 
    def __exit__(self, type, value, traceback): 
     pass 
    @property 
    def timed_out(self): 
     return time.time() > self.die_after 

Dưới đây là một single-threaded ví dụ sử dụng:

with timeout(1) as t: 
    while True: # this will take a long time without a timeout 
     # periodically check for timeouts 
     if t.timed_out: 
      break # or raise an exception 
     # do some "useful" work 
     print "." 
     time.sleep(0.2) 

và đa luồng một:

import thread 
def print_for_n_secs(string, seconds): 
    with timeout(seconds) as t: 
     while True: 
      if t.timed_out: 
       break # or raise an exception 
      print string, 
      time.sleep(0.5) 

for i in xrange(5): 
    thread.start_new_thread(print_for_n_secs, 
          ('thread%d' % (i,), 2)) 
    time.sleep(0.25) 

Cách tiếp cận này là khó khăn hơn so sử dụng tín hiệu nhưng nó hoạt động cho các chủ đề tùy ý.

+0

Đó là một cách tiếp cận có thể nhưng không quá ngắn và rõ ràng như tôi muốn nhận được.Biến thể của bạn đòi hỏi phải bọc mã với chức năng như trang trí, nhưng đó là phương pháp mới cho tôi, vì vậy tôi đã cho bạn tiền thưởng. Cảm ơn. – San4ez

0

Tôi biết đã muộn nhưng tôi chỉ đọc phần này, nhưng còn cách tạo người quản lý ngữ cảnh/người quản lý ngữ cảnh của riêng bạn thì sao? Tôi mới đến python sẽ yêu thích phản hồi từ những người có kinh nghiệm thực hiện điều này.

này được dựa tắt của câu trả lời từ "ông Fooz"

class TimeoutSignaller(Thread): 
    def __init__(self, limit, handler): 
     Thread.__init__(self) 
     self.limit = limit 
     self.running = True 
     self.handler = handler 
     assert callable(handler), "Timeout Handler needs to be a method" 

    def run(self): 
     timeout_limit = datetime.datetime.now() + datetime.timedelta(seconds=self.limit) 
     while self.running: 
      if datetime.datetime.now() >= timeout_limit: 
       self.handler() 
       self.stop_run() 
       break 

    def stop_run(self): 
     self.running = False 

class ProcessContextManager: 
    def __init__(self, process, seconds=0, minutes=0, hours=0): 
     self.seconds = (hours * 3600) + (minutes * 60) + seconds 
     self.process = process 
     self.signal = TimeoutSignaller(self.seconds, self.signal_handler) 

    def __enter__(self): 
     self.signal.start() 
     return self.process 

    def __exit__(self, exc_type, exc_val, exc_tb): 
     self.signal.stop_run() 

    def signal_handler(self): 
     # Make process terminate however you like 
     # using self.process reference 
     raise TimeoutError("Process took too long to execute") 

Sử dụng trường hợp:

with ProcessContextManager(my_proc) as p: 
    # do stuff e.g. 
    p.execute() 
Các vấn đề liên quan