Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python线程池执行器线程未完成_Python_Multithreading_Concurrency_Sqlalchemy - Fatal编程技术网

Python线程池执行器线程未完成

Python线程池执行器线程未完成,python,multithreading,concurrency,sqlalchemy,Python,Multithreading,Concurrency,Sqlalchemy,我有一个使用Python 3.8.2使用concurrent.futures.ThreadPoolExecutor对页面进行爬网的脚本。从本质上讲,它会在一个页面上抓取链接,使用sqlalchemy将它们存储在sqlite中,然后再移动到下一个页面 我有一个问题,但脚本永远不会完成。我已经确保使用两个print语句完成所有进程,但是脚本只是挂起,从未完成。关于如何处理并发性和sqlite会话,我是否遗漏了什么 from sqlalchemy import create_engine, Colum

我有一个使用Python 3.8.2使用concurrent.futures.ThreadPoolExecutor对页面进行爬网的脚本。从本质上讲,它会在一个页面上抓取链接,使用sqlalchemy将它们存储在sqlite中,然后再移动到下一个页面

我有一个问题,但脚本永远不会完成。我已经确保使用两个print语句完成所有进程,但是脚本只是挂起,从未完成。关于如何处理并发性和sqlite会话,我是否遗漏了什么

from sqlalchemy import create_engine, Column, String
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base


def crawl(link):
    print('Starting: {}'.format(link))
    session = Session()
    html = requests.get(url, timeout=10)
    soup = BeautifulSoup(html.text, 'lxml')

    links = [entry.get('href') for entry in soup.find_all('a',  clazz)]
    for link in links:
        data = {
            'type': self.type,
            'status': self.status,
            'url': link
        }
        if not session.query(exists().where(Table.url == link)).scalar():
            d = DataEntry(**data)
            session.add(d)
            session.commit()

    print('Finished: {}'.format(link))

def main():
    links = ['www.link1.com', 'www.link2', ....]
    with futures.ThreadPoolExecutor(max_workers=4) as executor:
        the_futures = [executor.submit(crawl_for_listings, task) for task in tasks]
        for future in the_futures:
            try:
                result = future.result()
            except Exception as e:
                print('Thread threw exception:', e)

if __name__ == "__main__":
    engine = create_engine("sqlite:///database.sql")
    Base = declarative_base()

    class Links(Base):
        __tablename__ = 'links'

        url = Column(String, primary_key=True)
        type = Column(String)
        status = Column(String)

    Base.metadata.create_all(engine)

    session_factory = sessionmaker(bind=engine)
    Session = scoped_session(session_factory)

    main()

    Session.remove()

您对
submit
的呼叫应该是:

future = executor.submit(crawl, link)
不是:

在第一种情况下,您将向
submit
传递对函数及其参数的引用。在第二种情况下,首先调用函数,然后将该调用的返回值传递给
submit
,返回值显示为
None
。然后,您应该保存返回的
future
对象,并且可以在线程出现时测试线程的完成情况,因此:

with futures.ThreadPoolExecutor(max_workers=4) as executor: 
    the_futures = []
    for link in links:
        future = executor.submit(crawl, link)
        the_futures.append(future)
    for future in futures.as_completed(the_futures):
        #print(future.result()) # result is None in this case
        pass
或更“Pythonically”:

还请注意,我正在使用上下文管理器创建变量
executor
,以便在块终止时完成任何必要的清理(调用
shutdown
,这将等待所有未来完成,但我在退出块之前显式地等待未来完成)

如果您注意按创建顺序返回结果(在这种情况下,您不会这样做,因为返回的结果总是
None
):

但是,当您想要获得所有结果时,上面的
executor.map
函数就不那么方便了,而且一个或多个线程可能会引发异常,因为您将无法从引发异常的第一个线程之外的线程检索结果(即使假设您使用
try/except
块来获取结果)。当您调用的函数使用的不是一个参数时,使用它也会更加复杂。因此,在这种情况下,最好使用futures:

with futures.ThreadPoolExecutor(max_workers=4) as executor: 
    the_futures = [executor.submit(crawl, link) for link in links]
for future in the_futures:
    try:
        result = future.result() # could throw an exception if the thread threw an exception
        print(result)
    except Exception as e:
        print('Thread threw exception:', e)

有了以上所有内容,我仍然不确定为什么您的程序没有终止。有一件事是肯定的:您不是多线程。

我建议检查这个问题:
任务现在是如何定义的?也许您应该显示更多的代码。这只是我写错了,应该是链接。这只是一个我想从中提取列出的URL的字符串。感谢您的输入。您是对的,这对我来说是一个新领域,所以我可能仍然有错误。尽管如此,我仍然有同样的问题,我想知道这是否与sqlalchemy会话有关。我曾评论说我认为
会话.close()
爬网中
可能是问题所在。但我已经找到了一些例子,说明应该在哪里进行爬网。根据我在web上的发现(不是我个人的经验),代码看起来是正确的。你的线程中有没有出现异常而失败的?但是想到异常,我总的来说会想为什么会这样(这不一定能解决您的问题)如果线程可能引发异常,那么您最好不要使用
executor.map
函数,而应该使用
executor.submit
,它返回一个
future
。我已经更新了我的答案来解释这一点。现在我已经让它工作了。似乎会话.close()这就是问题所在。至少我的代码运行了很多次,我尝试了很多次迭代来确保,数据被添加到了我的数据库中。考虑到这么多人建议使用session.close()…我查看了一些示例,他们似乎使用了
session.close()
,从您执行
session=session()时起,我就开始质疑这一点
,我认为每个线程都获得相同的会话对象。请参阅:。方式1代码使用每个线程的会话和函数
thread\u worker
中的上下文管理器,以确保在函数结束之前,它将首先在会话上调用
close
。但使用
作用域\u会话的方式2代码则没有调用
close
with futures.ThreadPoolExecutor(max_workers=4) as executor: 
    the_futures = [executor.submit(crawl, link) for link in links]
    for future in futures.as_completed(the_futures):
        pass
with futures.ThreadPoolExecutor(max_workers=4) as executor: 
    for result in executor.map(crawl, links):
        #print(result) # None in this case
        pass
with futures.ThreadPoolExecutor(max_workers=4) as executor: 
    the_futures = [executor.submit(crawl, link) for link in links]
for future in the_futures:
    try:
        result = future.result() # could throw an exception if the thread threw an exception
        print(result)
    except Exception as e:
        print('Thread threw exception:', e)