悬挂线程和例外

时间:2017-11-29 02:26:40

标签: python-3.x python-multithreading

以下代码应该对FTP服务器进行身份验证,并检查是否允许写访问。只要使用单个主机,它就可以正常运行。

import ftplib
import queue
import threading


def check_ftp(options):
    host = options.get('host')
    user = options.get('user')
    passwd = options.get('passwd')
    port = options.get('port')
    # Attempt to connect and authenticate
    ftp = ftplib.FTP()
    try:
        ftp.connect(host, port, timeout=5)
        ftp.login(user, passwd)
        print('{}:{} - Login successful.'.format(host, port))
    except(ftplib.error_perm, OSError) as e:
        print('{}:{} - Unable to connect/auth: {}'.format(host, port, e))
        return
    # Attempt to write & get dir listing
    is_writable = False
    contents = []
    try:
        test_folder = 'test_folder'
        ftp.mkd(test_folder)
        print('{}:{} - FTP root is writable'.format(host, port))
        is_writable = True
        ftp.retrlines('LIST', contents.append)
        ftp.rmd(test_folder)
    except ftplib.error_perm as e:
        ftp.retrlines('LIST', contents.append)
        print('{}:{} - Not writable: {}'.format(host, port, e))
    ftp.quit()


class ModuleRunner(threading.Thread):

    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        while True:
            # gets the options from the queue
            options = self.queue.get()
            # run the module
            check_ftp(options)

            # Let queue know the job is one
            self.queue.task_done()


def run(thread_count):
    options =[ 
        {'host': 'ftp.uconn.edu',
         'user': 'anonymous',
         'passwd': 'anonymous@test.com',
         'port': 21},
         {'host': 'speedtest.tele2.net',
         'user': 'anonymous',
         'passwd': 'anonymous@test.com',
         'port': 21},
         {'host': 'test.talia.net',
         'user': 'anonymous',
         'passwd': 'anonymous@test.com',
         'port': 21},
    ]
    # Fill queue
    q = queue.Queue()
    for opt in options:
        q.put(opt)
    # Create a thread pool
    threads = thread_count
    for i in range(threads):
        t = ModuleRunner(q)
        t.setDaemon(True)
        t.start()
    q.join()

if __name__ == '__main__':
    run(1)

但是,如果添加了03个或更多主机(请参阅options变量),则每次脚本都会崩溃并挂起,即使使用了一个线程也是如此。错误:

ftp.uconn.edu:21 - Login successful.
ftp.uconn.edu:21 - Not writable: 550 test_folder: Permission denied
speedtest.tele2.net:21 - Login successful.
speedtest.tele2.net:21 - Not writable: 550 Permission denied.
test.talia.net:21 - Login successful.
Exception in thread Thread-1:
Traceback (most recent call last):
  File "ftp.py", line 25, in check_ftp
    ftp.mkd(test_folder)
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 641, in mkd
    resp = self.voidcmd('MKD ' + dirname)
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 276, in voidcmd
    return self.voidresp()
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 249, in voidresp
    resp = self.getresp()
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 244, in getresp
    raise error_perm(resp)
ftplib.error_perm: 550 test_folder: Permission denied

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 916, in _bootstrap_inner
    self.run()
  File "ftp.py", line 47, in run
    check_ftp(options)
  File "ftp.py", line 31, in check_ftp
    ftp.retrlines('LIST', contents.append)
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 466, in retrlines
    with self.transfercmd(cmd) as conn, \
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 397, in transfercmd
    return self.ntransfercmd(cmd, rest)[0]
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 359, in ntransfercmd
    source_address=self.source_address)
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/socket.py", line 722, in create_connection
    raise err
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/socket.py", line 713, in create_connection
    sock.connect(sa)

我不明白为什么套接字超时,即使检查在一个线程下运行。另外,如果一个线程挂起太长时间怎么能终止它。提前谢谢。

1 个答案:

答案 0 :(得分:1)

首先,这是有效的:

def run():
    check_ftp(options[0])
    check_ftp(options[1])
    check_ftp(options[2])

我猜不了。看来FTP对象没有正确清理。使用with操作可以更轻松地关闭FTP。见this example

from ftplib import FTP
with FTP("ftp1.at.proftpd.org") as ftp:
    ftp.login()
    ftp.dir()

此外,线程无法取消,但进程可以。切换到multiprocessing库将允许它们被杀死。