最近公司内部网络经常出问题,奇慢无比,导致人脸检测程序在下载图片时经常卡住,为了不影响数据的核对, 决定在网络不佳图片下载超时后放弃下载,继续执行后续程序。

于是整理出解决思路如下:

  1、在线程中完成图片下载任务

  2、设置图片下载超时的时间

  3、当下载超时后线束下载线程, 执行后续任务

为了便于演示下载效果, 决定采集requests请求方法, 而不用urltrieve下载

一、先看看单线程如何下载图片的问题

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __author__:kzg import threading
import time
from urllib.request import urlretrieve def callbackinfo(down, block, size):
'''
回调函数:
down:已经下载的数据块
block:数据块的大小
size:远程文件的大小
'''
per = 100.0 * (down * block) / size
if per > :
per =
time.sleep() # sleep 1秒
print('%.2f%%' % per)

# 图片下载函数
def downpic(url):
urlretrieve(url, 'test.jpg', callbackinfo) url = 'https://s1.tuchong.com/content-image/201909/98cac03c4a131754ce46d51faf597230.jpg'
# 执行线程
t = threading.Thread(target=downpic, args=(url,))
t.start()
t.join()
print("down OK") 结果:
0.00%
1.51%
down OK
3.02%
4.52%
6.03%
……

  可以看到,执行过程

    1、将图片下载程序塞到线程中执行

    2、启动线程

    3、三秒后线程仍未执行完,放弃阻塞

    4、执行print

    5、线程继续执行, 直到完成

二、守护线程(deamon)

    守护线程结束, 其中的子线程也被迫结束

#!/usr/bin/env python3
# -*- coding:utf- -*-
# __author__:kzg import threading
import time
from urllib.request import urlretrieve def callbackinfo(down, block, size):
'''
回调函数:
down:已经下载的数据块
block:数据块的大小
size:远程文件的大小
'''
per = 100.0 * (down * block) / size
if per > :
per =
time.sleep()
print('%.2f%%' % per) def downpic(url):
urlretrieve(url, 'test.jpg', callbackinfo) def mainFunc(funcname, args):
'''
:param funcname: 函数名(图片下载函数)
:param args: 参数(url地址)
:return:
'''
t = threading.Thread(target=funcname, args=(args,))
t.start() # 开始执行线程
t.join(timeout=) # 5秒后线程仍未执行完则放弃阻塞, 继续执行后续代码 url = 'https://s1.tuchong.com/content-image/201909/98cac03c4a131754ce46d51faf597230.jpg' m = threading.Thread(target=mainFunc, args=(downpic, url))
m.setDaemon(True)
m.start()
m.join() 结果:
0.00%
1.51%
3.02%
4.52%

  可以看到执行结果:

    1、mainfunc函数被塞到m线程中

    2、m线程设置为守护线程

    3、启动守护线程

    4、mainfunc下的子线程 t在5秒后仍未执行完,

        放弃阻塞,执行后续程序

        m.join被执行, 守护线程结束,子线程t 被迫结束(结果中只有图片只下载了4秒)

        图片中止下载

  按说到此为止应该圆满结束了, 然而在程序执行过程中发现子线程超时后, 确实开始执行后续代码,但子线程并未退出,仍然在运行。 经过不断排查发现问题出现在for循环上, 原来for循环也类似一个demon的线程,如果for循环一直不结束, 其内的子线程就不会结束。

三、遇到问题, 子线程未被关闭

#!/usr/bin/env python3
# -*- coding:utf- -*-
# __author__:kzg import threading
import time
from urllib.request import urlretrieve def callbackinfo(down, block, size):
'''
回调函数:
down:已经下载的数据块
block:数据块的大小
size:远程文件的大小
'''
per = 100.0 * (down * block) / size
if per > :
per =
time.sleep()
print('%.2f%%' % per) # 图片下载函数
def downpic(url):
urlretrieve(url, 'test.jpg', callbackinfo) def mainFunc(funcname, args):
'''
:param funcname: 函数名(图片下载函数)
:param args: 参数(url地址)
:return:
'''
t = threading.Thread(target=funcname, args=(args,))
t.start() # 开始执行线程
t.join(timeout=) # 3秒后线程仍未执行完则放弃阻塞, 继续执行后续代码 for i in range():
if i == :
url = 'https://s1.tuchong.com/content-image/201909/98cac03c4a131754ce46d51faf597230.jpg'
else:
break
# 守护线程
m = threading.Thread(target=mainFunc, args=(downpic, url))
m.setDaemon(True)
m.start()
m.join()
print(m.is_alive())
time.sleep() # sleep 100秒, 模拟for一直不结束 结果:
0.00%
1.51%
3.02%
4.52%
False
6.03%
7.54%
9.05%
10.55%

  从结果可以看出, 5秒后deamon线程结束, 意味着 t 线程会被关闭,然而子线程 t 却一直在执行。

  怎么办呢?

四、问题解决, 强制关闭子线程

#!/usr/bin/env python3
# -*- coding:utf- -*-
# __author__:kzg import threading
import time
import inspect
import ctypes
from urllib.request import urlretrieve def callbackinfo(down, block, size):
'''
回调函数:
down:已经下载的数据块
block:数据块的大小
size:远程文件的大小
'''
per = 100.0 * (down * block) / size
if per > :
per =
time.sleep()
print('%.2f%%' % per) # 图片下载函数
def downpic(url):
urlretrieve(url, 'test.jpg', callbackinfo) def _async_raise(tid, exctype):
"""raises the exception, performs cleanup if needed"""
tid = ctypes.c_long(tid)
if not inspect.isclass(exctype):
exctype = type(exctype)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
if res == :
raise ValueError("invalid thread id")
elif res != :
# """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, None)
raise SystemError("PyThreadState_SetAsyncExc failed") def stop_thread(thread):
_async_raise(thread.ident, SystemExit) for i in range():
if i == :
url = 'https://s1.tuchong.com/content-image/201909/98cac03c4a131754ce46d51faf597230.jpg'
else:
break
t = threading.Thread(target=downpic, args=(url,))
t.start()
t.join()
print(t.is_alive())
if t.is_alive():
stop_thread(t)
print("t is kill")
time.sleep() 结果:
0.00%
1.51%
3.02%
4.52%
True
t is kill

  可以看到:

    1、 主函数mainfunc去掉了

    2、在for循环中直接加入子线程

    3、在timeout的时间后线程仍然活着则强制关闭

附: 测试图片下载的另一种方法

#!/usr/bin/python3
# -*- coding: utf- -*-
import requests
import os
import time def downpic(url):
'''
根据url下载图片
:param url: url地址
:return: 下载后的图片名称
'''
try:
print("Start Down %s" % url)
ret = requests.get(url, timeout=) # 请求超时
if ret.status_code == :
with open("test.jpg", 'wb') as fp:
for d in ret.iter_content(chunk_size=):
time.sleep() # 每次下载10k,sleep 1秒
fp.write(d)
print("downLoad ok %s" % url)
except Exception as ex:
print("downLoad pic fail %s" % url)

其它:

urlretrieve第三个参数为reporthook:
是一个回调函数,当连接上服务器以及相应数据块传输完毕时会触发该回调,我们就可以利用该回调函数来显示当前的下载进度。
    下载状态的报告,他有多个参数,
    1)参数1:当前传输的块数
    2)参数2:块的大小
    3)参数3,总数据大小
def urlretrieve(url, filename=None, reporthook=None, data=None):
"""
Retrieve a URL into a temporary location on disk. Requires a URL argument. If a filename is passed, it is used as
the temporary file location. The reporthook argument should be
a callable that accepts a block number, a read size, and the
total file size of the URL target. The data argument should be
valid URL encoded data. If a filename is passed and the URL points to a local resource,
the result is a copy from local file to new file. Returns a tuple containing the path to the newly created
data file as well as the resulting HTTPMessage object.
"""

python3 结束子线程的更多相关文章

  1. iOS中安全结束 子线程 的方法

    一个典型的结束子线程的方法:   用 isFinished 检测子线程是否被完全kill掉 -(IBAction)btnBack:(id)sender { //释放内存 仅仅remove 并不会触发内 ...

  2. C# WinForm 多线程 应用程序退出的方法 结束子线程

    1.this.Close(); 只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外若有托管线程(非主线程),也无法干净地退出: 2.Application.Exit(); 强制所有消息中止,退 ...

  3. Pytrhon结束死循环的子线程

    Python在子线程无线循环的过程中,如果直接ctrl+c结束程序的话,虽然程序可以结束,但是会导致子线程资源无法回收,一般情况不会有太大影响,但是使用TCP通信的时候,子线程是占用特定的端口的,在资 ...

  4. iOS中使用子线程的完整方法

    http://www.cnblogs.com/ygm900/archive/2013/06/23/3151691.html 第一步:开启子线程 //开启子线程到网络上获取数据 myFirstThrea ...

  5. Java主线程等待子线程、线程池

    public class TestThread extends Thread { public void run() { System.out.println(this.getName() + &qu ...

  6. Java并发编程原理与实战六:主线程等待子线程解决方案

    本文将研究的是主线程等待所有子线程执行完成之后再继续往下执行的解决方案 public class TestThread extends Thread { public void run() { Sys ...

  7. android动画之通过子线程来实现动画

    android动画之通过子线程来实现动画 使用android动画机制,往往是相对于原始位置来进行参照. 这里通过子线程修改物体位置实现动画. 布局文件: <RelativeLayout xmln ...

  8. C#主线程等待子线程运行结束

    佐左佑右 原文 C#主线程等待子线程运行结束 由于主程序中调用matlab的dll文件进行计算要用较长的时间,主界面会有很长时间的卡顿,造成的用户感受十分不好,因此我想在调用时,将调用放入子线程中,然 ...

  9. Java如何等待子线程执行结束

    工作中往往会遇到异步去执行某段逻辑, 然后先处理其他事情, 处理完后再把那段逻辑的处理结果进行汇总的产景, 这时候就需要使用线程了. 一个线程启动之后, 是异步的去执行需要执行的内容的, 不会影响主线 ...

随机推荐

  1. 3.02定义常量之const

    [注:本程序验证是使用vs2013版] #include <stdio.h> #include <stdlib.h> #include <string.h> #pr ...

  2. Python之TensorFlow的(案例)验证码识别-6

    一.这里的案例相对比较简单,主要就是通过学习验证码的识别来认识深度学习中我们一般在工作中,需要处理的东西会存在哪些东西. 二.因为我没有数据集,没有关系,这里自己写了一个数据集,来做测试,为了方便我把 ...

  3. 湖南师范大学计算机基础课网络教学平台 版本 V2.0(2017.9.18)

    湖南师范大学计算机基础课网络教学平台 版本 V2.0(2017.9.18) 开发环境: 开发工具:VS2013,数据库:Sqlserver2012 开发语言:Asp.net MVC5 ,界面UI:jq ...

  4. 跨域访问MVC

    using MvcApp.Filters; using System; using System.Collections.Generic; using System.Linq; using Syste ...

  5. android 常用库的地址--dialog,recycler

    android 弹出框     https://github.com/li-xiaojun/XPopup android  RecyclerViewAdapter     https://github ...

  6. 从ghost映像.gho文件快速创建vmware虚拟机

    从ghost映像.gho文件快速创建vmware虚拟机 https://www.cnblogs.com/blog2018/p/8857146.html ghost文件.gho和vmware文件都是磁盘 ...

  7. android 给ImageView设置路径

    ImageView是Android程序中经常用到的组件,它将一个图片显示到屏幕上. 在UI xml定义一个ImageView如下: public void onCreate(Bundle savedI ...

  8. iOS配置TARGETS

    说一下背景 自从这个项目不死不活的迭代了2年,从项目搭建到现在,一直都是自己在开发和维护,所以项目结构非常清晰,但是之前的水平写的代码现在看来也是惨不忍睹,不过本人比较懒,也就没有考虑过重构的事情 - ...

  9. 使用企业证书给iOS应用重签

    来源:https://github.com/sailtsao/iReSign 这里有个开源的签名工具,已经修改为支持dylib frameworks等的签名了,使用这个签名不会出现任何问题 iReSi ...

  10. 服务网关ZuulFilter过滤器--pre/post/error的用法(校验请求信息,获取路由后的请求/响应信息,处理服务网关异常)

    微服务中Zuul服务网关一共定义了四种类型的过滤器: pre:在请求被路由(转发)之前调用 route:在路由(请求)转发时被调用 error:服务网关发生异常时被调用 post:在路由(转发)请求后 ...