深入理解python--线程、进程与协程

(1)线程的概念
线程是进程内同步执行的代码段,每个线程对应一个栈。

(2)创建线程的2种方式

方式1:该方式常用(通过Thread类的args参数调用线程函数)
注意:线程函数传入的是元组类型,如果只有一个参数,需要加入逗号.

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
#创建线程的第一种方式.
“”"

from threading import Thread

def task(arg):
print(arg)

#创建2个线程对象.
t1 = Thread(target=task,args=(1,))
t2 = Thread(target=task,args=(2,))

t1.start()
t2.start()

print(“主线程”)
实例2:

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
#创建线程的第一种方式.
“”"

from threading import Thread

def task(arg):
print(“==================================>”)
print(arg)

#创建2个线程对象.
for i in range(30):
t = Thread(target=task,args=(i,))
#启动线程.
t.start()

print(“主线程”)

方式2:继承Thread类,重写run方法(同Java,但是在python当中很少用这种方式)
#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
#创建线程的第2种方式:继承thread类,重写run方法.
“”"

from threading import Thread
import time

class MyThread(Thread):
#初始化构造方法不写也可以.
def init(self,*args,**kwargs):
super().init(*args,**kwargs)

#重写Thread当中的run方法.
def run(self):
    for i in range(0,5):
        print("%s"%(self.getName()),i)

t1 = MyThread()
t1.setName(“线程1”)
t2 = MyThread()
t2.setName(“线程2”)

t1.start()
t2.start()

for i in range(0,5):
print(i)
(3)threading.local()方法介绍
(4)Python线程当中常用的一些方法
join():
如果一个线程A调用了某个线程B对象的join() 方法,此时执行代码的线程A将会从可运行状态(RUNNABLE)转变为无限时等待状态

(WAITING)状态,直到线程B执行完之后,等待它的线程A才会从无限时等待状态(WAITING)状态转变为可运行状态(RUNNABLE);

如果一个线程调用了有参数的的join方法,那么这个线程就会从可运行状态(RUNNABLE)转变为有限时等待状态(TIMED_WAITING),

该状态不同于WAITING,在达到一定时间的后它们会自动唤醒,即从有限时等待状态(TIMED_WAITING)转变为可运行状态(RUNNABLE);

threading.current_thread():当前线程对象

threading.current_thread().name:当前线程的名字

实例程序1:

#!/usr/bin/env python3

-- coding:utf-8 --

from threading import Thread
import time

def task(arg):
time.sleep(arg)
print(arg)

for i in range(0,5):
t = Thread(target=task,args=(i,))
t.start()
t.join() #这个地方加会并行变为串行.

print(“>主线程==>”)
实例程序2:只有其余的线程执行完毕,主线程才会执行完毕,同Java.

#!/usr/bin/env python3

-- coding:utf-8 --

from threading import Thread
import time

def func(*args,**kwargs):
print(args)
time.sleep(args[0])

thread_list = []

for i in range(0,5):
t = Thread(target=func,args=(i,))
t.start()
thread_list.append(t)

#只有其余的线程都运行完毕,主线程才会运行完毕.
for r in thread_list:
r.join()

print(“>主线程====>”)

(6)守护线程
daemon:守护线程,类似于海底捞的服务员,守护线程守护非守护线程,这种线程一般用于服务类线程.

如果进程中剩余的线程都是守护线程,那么该进程就结束了.

实例程序:

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
#创建线程的第一种方式.
“”"

from threading import Thread
import time

def func(*args,**kwargs):
time.sleep(1)
print(args)
print(kwargs)

for i in range(0,30):
t = Thread(target=func,args=(i,))
t.setDaemon(True) #守护线程守护的是非守护线程,如果进程中剩余的线程都是守护线程,那么该进程就结束了.
t.start()

print(“主线程!”)
print(“主线程!”)
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/Teacher.py”
主线程!
主线程!

Process finished with exit code 0

(8)线程(同步)安全的概念以及不安全导致的原因
所谓线程同步(安全)的问题就是多个线程在处理相同资源的时候,要保证共享数据的数据一致性和变化一致性;

导致线程同步异常的原因共有两个:

①多个线程彼此之间处理的是相同的资源;

②多个线程彼此之间在处理相同关键步骤的时候,某个线程在这些关键的步骤没有执行完毕的时候,CPU切换到了另外一个线程去执行

这些关键的步骤,导致共享数据的一致性出现问题.

示例程序:买票异常程序

#!/usr/bin/env python3

-- coding:utf-8 --

import threading
import time

tickets = 100

def saler():
while tickets >= 1:
global tickets
print(“当前线程:%s 正在售第:%s张票!”%(threading.current_thread().name,tickets))
tickets -= 1
time.sleep(1)

t1 = threading.Thread(target=saler,args=())
t1.setName(“售票员1”)
t2 = threading.Thread(target=saler,args=())
t2.setName(“售票员2”)

t1.start()
t2.start()

print(“+++++++++++主线程+++++++++++++++”)
异常运行结果:

(9)线程安全的解决方案:互斥锁
线程同步(安全)的问题是通过线程锁机制来解决的,通过线程锁机制,保证这些关键的步骤在被某一个线程执行的时候,

将不允许其它线程来执行这些步骤,直到该线程将这些关键的步骤执行完毕,才允许其他线程执行这些步骤.

使用lock = threading.Lock()函数创建线程锁、使用lock.acquire()方法加锁、使用lock.release()方法解锁.

实例程序1:

#!/usr/bin/env python3

-- coding:utf-8 --

import threading
import time

tickets = 100

lock = threading.Lock()

def saler():
while tickets >= 1:
global tickets
#加锁.
lock.acquire()
print(“当前线程:%s 正在售第:%s张票!”%(threading.current_thread().name,tickets))
tickets -= 1
#释放锁.
lock.release()
time.sleep(1)

t1 = threading.Thread(target=saler,args=())
t1.setName(“售票员1”)
t2 = threading.Thread(target=saler,args=())
t2.setName(“售票员2”)

t1.start()
t2.start()

print(“+++++++++++主线程+++++++++++++++”)

运行结果:

pass

(10)semaphore信号量
信号量是一个计数器,用于记录资源的消耗情况,当资源消耗时递减,当资源释放时递增,可以认为信号量代表资源是否可用。

semaphore是一个内置的计数器

每当调用acquire()时,内置计数器-1

每当调用release()时,内置计数器+1

计数器不能小于0,当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

互斥锁:同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,

后面的人只能等里面有人出来了才能再进去。

可以这么理解:同一时刻可以有n个线程可以使用到锁!

实例程序1:

#!/usr/bin/env python3

-- coding:utf-8 --

import threading

semaphore = threading.BoundedSemaphore(3)

semaphore.acquire() #使用资源
print(semaphore._value)
semaphore.acquire()
print(semaphore._value)
semaphore.acquire()
print(semaphore._value)

semaphore.release() #释放资源.
print(semaphore._value)
semaphore.release()
print(semaphore._value)
semaphore.release()
print(semaphore._value)
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/Teacher.py”
2
1
0
1
2
3

Process finished with exit code 0

实例程序2:

#!/usr/bin/env python3

-- coding:utf-8 --

import threading
import time

semaphore = threading.BoundedSemaphore(3)

def func(*args,**kwargs):
print(args,kwargs,time.ctime())
time.sleep(2)

for i in range(0,10):
t = threading.Thread(target=func,args=(i,))
t.start()

print(“>主线程>”)
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/Teacher.py”
(0,) {} Wed Feb 5 18:13:17 2020
(1,) {} Wed Feb 5 18:13:17 2020
(2,) {} Wed Feb 5 18:13:17 2020
(3,) {} Wed Feb 5 18:13:17 2020
(4,) {} Wed Feb 5 18:13:17 2020
(5,) {} Wed Feb 5 18:13:17 2020
(6,) {} Wed Feb 5 18:13:17 2020
(7,) {} Wed Feb 5 18:13:17 2020
(8,) {} Wed Feb 5 18:13:17 2020
(9,) {} Wed Feb 5 18:13:17 2020
>主线程>

Process finished with exit code 0

可以看到,程序会在很短的时间内生成10个线程来打印一句话,如果在主机执行IO密集型任务的时候再执行这种类型的程序时,计算机就有很大可能会宕机。

这时候就可以为这段程序添加一个计数器功能,来限制一个时间点内的线程数量。

优化程序:

#!/usr/bin/env python3

-- coding:utf-8 --

import threading
import time

#同时有三个线程可以使用锁,进行处理,即多个人同时使用锁:信号量.
semaphore = threading.BoundedSemaphore(3)

def func(*args,**kwargs):
semaphore.acquire()
print(args,kwargs,time.ctime())
time.sleep(2)
semaphore.release()

for i in range(0,10):
t = threading.Thread(target=func,args=(i,))
t.start()

print(“>主线程>”)

运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/Teacher.py”
(0,) {} Wed Feb 5 18:15:24 2020
(1,) {} Wed Feb 5 18:15:24 2020
(2,) {} Wed Feb 5 18:15:24 2020
>主线程>
(3,) {} Wed Feb 5 18:15:26 2020
(4,) {} Wed Feb 5 18:15:26 2020
(5,) {} Wed Feb 5 18:15:26 2020
(7,) {} Wed Feb 5 18:15:28 2020
(6,) {} Wed Feb 5 18:15:28 2020
(8,) {} Wed Feb 5 18:15:28 2020
(9,) {} Wed Feb 5 18:15:30 2020

Process finished with exit code 0
形象例子:上厕所.

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"

from threading import BoundedSemaphore
import threading

semaphore = BoundedSemaphore(3)

def go_wc(name):
semaphore.acquire()
print(“%s 正在上厕所!”%(name))
import time
time.sleep(10)
print(“上厕所完毕!”)
semaphore.release()

if name == ‘main’:
for i in range(0,5):
t = threading.Thread(target=go_wc,args=(i,))
t.start()

print("++++++++++主线程++++++++++++")

(11)线程其余锁介绍
a.递归锁 threading.RLock()
lock = threading.Lock() 只能上一把,可以理解为进厕所自己只能上一把锁,但是别人不能进去.

lock = threading.RLock() 可以上多把,可以理解为进厕所自己可以上多把锁,同时别人不能进去.

#!/usr/bin/env python3

-- coding:utf-8 --

import threading
import time

tickets = 100

lock = threading.RLock()

def saler():
while tickets >= 1:
global tickets
# 加3把锁.
lock.acquire()
lock.acquire()
lock.acquire()
print(“当前线程:%s 正在售第:%s张票!” % (threading.current_thread().name, tickets))
tickets -= 1
print(time.ctime())
time.sleep(3)
# 释放3把锁.
lock.release()
lock.release()
lock.release()

t1 = threading.Thread(target=saler, args=())
t1.setName(“售票员1”)
t2 = threading.Thread(target=saler, args=())
t2.setName(“售票员2”)

t1.start()
t2.start()

print(“+++++++++++主线程+++++++++++++++”)

运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/Teacher.py”
D:/Python Work Location/DBS_NEW_@/DBS_NEW/Teacher.py:15: SyntaxWarning: name ‘tickets’ is used prior to global declaration
global tickets
当前线程:售票员1 正在售第:100张票!
Wed Feb 5 20:16:50 2020
+++++++++++主线程+++++++++++++++
当前线程:售票员2 正在售第:99张票!
Wed Feb 5 20:16:50 2020
当前线程:售票员2 正在售第:98张票!
Wed Feb 5 20:16:53 2020
当前线程:售票员1 正在售第:97张票!
Wed Feb 5 20:16:53 2020
当前线程:售票员2 正在售第:96张票!
Wed Feb 5 20:16:56 2020
当前线程:售票员1 正在售第:95张票!
Wed Feb 5 20:16:56 2020
当前线程:售票员2 正在售第:94张票!
Wed Feb 5 20:16:59 2020
当前线程:售票员1 正在售第:93张票!
Wed Feb 5 20:16:59 2020
当前线程:售票员2 正在售第:92张票!
Wed Feb 5 20:17:02 2020
当前线程:售票员1 正在售第:91张票!
Wed Feb 5 20:17:02 2020
当前线程:售票员1 正在售第:90张票!
Wed Feb 5 20:17:05 2020
当前线程:售票员2 正在售第:89张票!
Wed Feb 5 20:17:05 2020
当前线程:售票员1 正在售第:88张票!
Wed Feb 5 20:17:08 2020
当前线程:售票员2 正在售第:87张票!
Wed Feb 5 20:17:08 2020
当前线程:售票员1 正在售第:86张票!
Wed Feb 5 20:17:11 2020
当前线程:售票员2 正在售第:85张票!
Wed Feb 5 20:17:11 2020
当前线程:售票员1 正在售第:84张票!
Wed Feb 5 20:17:14 2020
当前线程:售票员2 正在售第:83张票!
Wed Feb 5 20:17:14 2020
当前线程:售票员1 正在售第:82张票!
Wed Feb 5 20:17:17 2020
当前线程:售票员2 正在售第:81张票!
Wed Feb 5 20:17:17 2020
当前线程:售票员1 正在售第:80张票!
Wed Feb 5 20:17:20 2020
当前线程:售票员2 正在售第:79张票!
Wed Feb 5 20:17:20 2020
当前线程:售票员2 正在售第:78张票!
Wed Feb 5 20:17:23 2020
当前线程:售票员1 正在售第:77张票!
Wed Feb 5 20:17:23 2020
当前线程:售票员2 正在售第:76张票!
Wed Feb 5 20:17:26 2020
当前线程:售票员1 正在售第:75张票!
Wed Feb 5 20:17:26 2020
当前线程:售票员2 正在售第:74张票!
Wed Feb 5 20:17:29 2020
当前线程:售票员1 正在售第:73张票!
Wed Feb 5 20:17:29 2020
当前线程:售票员2 正在售第:72张票!
Wed Feb 5 20:17:32 2020
当前线程:售票员1 正在售第:71张票!
Wed Feb 5 20:17:32 2020
当前线程:售票员1 正在售第:70张票!
Wed Feb 5 20:17:35 2020
当前线程:售票员2 正在售第:69张票!
Wed Feb 5 20:17:35 2020
当前线程:售票员2 正在售第:68张票!
Wed Feb 5 20:17:38 2020
当前线程:售票员1 正在售第:67张票!
Wed Feb 5 20:17:38 2020
当前线程:售票员2 正在售第:66张票!
Wed Feb 5 20:17:41 2020
当前线程:售票员1 正在售第:65张票!
Wed Feb 5 20:17:41 2020
当前线程:售票员2 正在售第:64张票!
Wed Feb 5 20:17:44 2020
当前线程:售票员1 正在售第:63张票!
Wed Feb 5 20:17:44 2020
当前线程:售票员2 正在售第:62张票!
Wed Feb 5 20:17:47 2020
当前线程:售票员1 正在售第:61张票!
Wed Feb 5 20:17:47 2020
当前线程:售票员2 正在售第:60张票!
Wed Feb 5 20:17:50 2020
当前线程:售票员1 正在售第:59张票!
Wed Feb 5 20:17:50 2020
当前线程:售票员1 正在售第:58张票!
Wed Feb 5 20:17:53 2020
当前线程:售票员2 正在售第:57张票!
Wed Feb 5 20:17:53 2020
当前线程:售票员2 正在售第:56张票!
Wed Feb 5 20:17:56 2020
当前线程:售票员1 正在售第:55张票!
Wed Feb 5 20:17:56 2020
当前线程:售票员2 正在售第:54张票!
Wed Feb 5 20:17:59 2020
当前线程:售票员1 正在售第:53张票!
Wed Feb 5 20:17:59 2020
当前线程:售票员2 正在售第:52张票!
Wed Feb 5 20:18:02 2020
当前线程:售票员1 正在售第:51张票!
Wed Feb 5 20:18:02 2020
当前线程:售票员1 正在售第:50张票!
Wed Feb 5 20:18:05 2020
当前线程:售票员2 正在售第:49张票!
Wed Feb 5 20:18:05 2020
当前线程:售票员2 正在售第:48张票!
Wed Feb 5 20:18:08 2020
当前线程:售票员1 正在售第:47张票!
Wed Feb 5 20:18:08 2020
当前线程:售票员1 正在售第:46张票!
Wed Feb 5 20:18:11 2020
当前线程:售票员2 正在售第:45张票!
Wed Feb 5 20:18:11 2020
当前线程:售票员1 正在售第:44张票!
Wed Feb 5 20:18:14 2020
当前线程:售票员2 正在售第:43张票!
Wed Feb 5 20:18:14 2020
当前线程:售票员2 正在售第:42张票!
Wed Feb 5 20:18:17 2020
当前线程:售票员1 正在售第:41张票!
Wed Feb 5 20:18:17 2020
当前线程:售票员1 正在售第:40张票!
Wed Feb 5 20:18:20 2020
当前线程:售票员2 正在售第:39张票!
Wed Feb 5 20:18:20 2020
当前线程:售票员1 正在售第:38张票!
Wed Feb 5 20:18:23 2020
当前线程:售票员2 正在售第:37张票!
Wed Feb 5 20:18:23 2020
当前线程:售票员2 正在售第:36张票!
Wed Feb 5 20:18:26 2020
当前线程:售票员1 正在售第:35张票!
Wed Feb 5 20:18:26 2020
当前线程:售票员2 正在售第:34张票!
Wed Feb 5 20:18:29 2020
当前线程:售票员1 正在售第:33张票!
Wed Feb 5 20:18:29 2020
当前线程:售票员1 正在售第:32张票!
Wed Feb 5 20:18:32 2020
当前线程:售票员2 正在售第:31张票!
Wed Feb 5 20:18:32 2020
当前线程:售票员2 正在售第:30张票!
Wed Feb 5 20:18:35 2020
当前线程:售票员1 正在售第:29张票!
Wed Feb 5 20:18:35 2020
当前线程:售票员2 正在售第:28张票!
Wed Feb 5 20:18:38 2020
当前线程:售票员1 正在售第:27张票!
Wed Feb 5 20:18:38 2020
当前线程:售票员2 正在售第:26张票!
Wed Feb 5 20:18:41 2020
当前线程:售票员1 正在售第:25张票!
Wed Feb 5 20:18:41 2020
当前线程:售票员1 正在售第:24张票!
Wed Feb 5 20:18:44 2020
当前线程:售票员2 正在售第:23张票!
Wed Feb 5 20:18:44 2020
当前线程:售票员2 正在售第:22张票!
Wed Feb 5 20:18:47 2020
当前线程:售票员1 正在售第:21张票!
Wed Feb 5 20:18:47 2020
当前线程:售票员2 正在售第:20张票!
Wed Feb 5 20:18:50 2020
当前线程:售票员1 正在售第:19张票!
Wed Feb 5 20:18:50 2020
当前线程:售票员2 正在售第:18张票!
Wed Feb 5 20:18:53 2020
当前线程:售票员1 正在售第:17张票!
Wed Feb 5 20:18:53 2020
当前线程:售票员2 正在售第:16张票!
Wed Feb 5 20:18:56 2020
当前线程:售票员1 正在售第:15张票!
Wed Feb 5 20:18:56 2020
当前线程:售票员1 正在售第:14张票!
Wed Feb 5 20:18:59 2020
当前线程:售票员2 正在售第:13张票!
Wed Feb 5 20:18:59 2020
当前线程:售票员1 正在售第:12张票!
Wed Feb 5 20:19:02 2020
当前线程:售票员2 正在售第:11张票!
Wed Feb 5 20:19:02 2020
当前线程:售票员1 正在售第:10张票!
Wed Feb 5 20:19:05 2020
当前线程:售票员2 正在售第:9张票!
Wed Feb 5 20:19:05 2020
当前线程:售票员1 正在售第:8张票!
Wed Feb 5 20:19:08 2020
当前线程:售票员2 正在售第:7张票!
Wed Feb 5 20:19:08 2020
当前线程:售票员2 正在售第:6张票!
Wed Feb 5 20:19:11 2020
当前线程:售票员1 正在售第:5张票!
Wed Feb 5 20:19:11 2020
当前线程:售票员2 正在售第:4张票!
Wed Feb 5 20:19:14 2020
当前线程:售票员1 正在售第:3张票!
Wed Feb 5 20:19:14 2020
当前线程:售票员2 正在售第:2张票!
Wed Feb 5 20:19:17 2020
当前线程:售票员1 正在售第:1张票!
Wed Feb 5 20:19:17 2020
。。。。
。。。。

b.事件(event)
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,

那么event.wait 方法时便不再阻塞。

clear:将Flag设置为False
set:将“Flag”设置为True
event_obj = threading.Event()

event_obj.wait() #让当前线程进入锁旗标的等待队列.

event_obj.set() #唤醒锁旗标等待队列当中的所有线程.

event_obj.clear():暂时未确定作用.

代码:

#!/usr/bin/env python3

-- coding:utf-8 --

import threading

event_obj = threading.Event()

def func(*args,**kwargs):
print(“start”)
event_obj.wait() #让当前线程进入锁旗标的等待队列.
print(“execute”)

for i in range(0,10):
t = threading.Thread(target=func,args=(10,20))
t.start()

print(“==>主线程=>”)
inp = input(‘请输入你的命令:’).strip()
if inp == ‘true’:
event_obj.set() #唤醒锁旗标等待队列当中的所有线程.
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/Teacher.py”
start
start
start
start
start
start
start
start
start
start
==>主线程=>
请输入你的命令:true
execute
execute
execute
execute
execute
execute
execute
execute
execute
execute

Process finished with exit code 0

c.条件(Condition)
参考:https://www.cnblogs.com/wupeiqi/articles/5040827.html

(12)线程池
参考博客:

https://www.cnblogs.com/xiao-apple36/p/9499000.html

https://www.jb51.net/article/170571.htm

在刚开始学多进程或多线程时,我们迫不及待地基于多进程或多线程实现并发的套接字通信,然而这种实现方式的致命缺陷是:服务的开启的

进程数或线程数都会随着并发的客户端数目地增多而增多,这会对服务端主机带来巨大的压力,甚至于不堪重负而瘫痪,于是我们必须对服务

端开启的进程数或线程数加以控制,让机器在一个自己可以承受的范围内运行,这就是进程池或线程池的用途,

例如进程池,就是用来存放进程的池子,本质还是基于多进程,只不过是对开启进程的数目加上了限制.

a. 池的作用
池的功能:限制进程数或线程数.

什么时候限制: 当并发的任务数量远远大于计算机所能承受的范围,即无法一次性开启过多的任务数量 我就应该考虑去限制我进程数或线程数,从

保证服务器不崩.

b. concurrent.futures
1.concurent.future模块是用来创建并行的任务,提供了更高级别的接口为了异步执行调用

2.concurent.future这个模块用起来非常方便,它的接口也封装的非常简单

3.concurent.future模块既可以实现进程池,也可以实现线程池

4.模块导入进程池和线程池

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor

p = ProcessPoolExecutor(max_works)对于进程池如果不写max_works:默认的是cpu的数目

p = ThreadPoolExecutor(max_works)对于线程池如果不写max_works:默认的是cpu的数目*5 (但是我并没有从源码看到.)

c. 基本方法
1、submit(fn, *args, **kwargs):异步提交任务

2、map(func, *iterables, timeout=None, chunksize=1): 取代for循环submit的操作

3、shutdown(wait=True):相当于进程池的pool.close()+pool.join()操作
wait=True,等待池内所有任务执行完毕回收完资源后才继续,源代码的内部其实就是通过join()方法来实现的:

wait=False,立即返回,并不会等待池内的任务执行完毕,
但不管wait参数为何值,整个程序都会等到所有任务执行完毕,
submit和map必须在shutdown之前

4、result(timeout=None):取得结果

5、add_done_callback(fn):回调函数

d. 同步和异步

理解为提交任务的两种方式

同步: 提交了一个任务,必须等任务执行完了(拿到返回值),才能执行下一行代码,相当于执行任务的串行执行,类似于Kafka的同步阻塞的方式:

此时并行会变成串行!,不建议这么用,没有什么意义了!

同步代码示例:

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"

!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"

from concurrent.futures import ThreadPoolExecutor
import threading
import os, time

def task(i):
print(“线程:%s 在运行当中…:%s” % (threading.current_thread().name, time.ctime()))
time.sleep(2)
return i * 2

pool = ThreadPoolExecutor(4)

res = []
for i in range(0, 10):
#同步提交方式,只有当前任务执行完毕之后,才能执行下一个任务,所以并行会变成串行,没有什么意义.
future = pool.submit(task, i)
print(future.result()) #一旦调用future.result(),相当于调用了kafka的future.get()

print(“++++++++++++++主线程继续执行++++++++++++++++++”)
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/test2.py”
线程:Thread-1 在运行当中…:Thu Feb 6 20:50:27 2020
0
线程:Thread-1 在运行当中…:Thu Feb 6 20:50:29 2020
2
线程:Thread-2 在运行当中…:Thu Feb 6 20:50:31 2020
4
线程:Thread-1 在运行当中…:Thu Feb 6 20:50:33 2020
6
线程:Thread-3 在运行当中…:Thu Feb 6 20:50:35 2020
8
线程:Thread-2 在运行当中…:Thu Feb 6 20:50:37 2020
10
线程:Thread-4 在运行当中…:Thu Feb 6 20:50:39 2020
12
线程:Thread-1 在运行当中…:Thu Feb 6 20:50:41 2020
14
线程:Thread-3 在运行当中…:Thu Feb 6 20:50:43 2020
16
线程:Thread-2 在运行当中…:Thu Feb 6 20:50:45 2020
18
++++++++++++++主线程继续执行++++++++++++++++++

Process finished with exit code 0

异步: 提交了一个任务,不要等执行完了,可以直接执行下一行代码,类似于Kafka的异步提交任务方式:这种方式也是我们推荐的!

代码示例:

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"

!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"

from concurrent.futures import ThreadPoolExecutor
import threading
import os, time

def task(i):
print(“线程:%s 在运行当中…:%s” % (threading.current_thread().name, time.ctime()))
time.sleep(2)
return i * 2

pool = ThreadPoolExecutor(4)

res = []
for i in range(0, 10):
#异步提交方式.
future = pool.submit(task, i)
res.append(future)

#主线程只有等待其余线程执行完毕之后,才会从无限时等待状态waiting转为可运行状态runnable
pool.shutdown(wait=True)

print(“++++++++++++++主线程继续执行++++++++++++++++++”)
for r in res:
print(r.result()) # 获取每个线程对应的执行结果.
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/test2.py”
线程:Thread-1 在运行当中…:Thu Feb 6 21:01:54 2020
线程:Thread-2 在运行当中…:Thu Feb 6 21:01:54 2020
线程:Thread-3 在运行当中…:Thu Feb 6 21:01:54 2020
线程:Thread-4 在运行当中…:Thu Feb 6 21:01:54 2020
线程:Thread-1 在运行当中…:Thu Feb 6 21:01:56 2020
线程:Thread-2 在运行当中…:Thu Feb 6 21:01:56 2020
线程:Thread-4 在运行当中…:Thu Feb 6 21:01:56 2020
线程:Thread-3 在运行当中…:Thu Feb 6 21:01:56 2020
线程:Thread-1 在运行当中…:Thu Feb 6 21:01:58 2020
线程:Thread-2 在运行当中…:Thu Feb 6 21:01:58 2020
++++++++++++++主线程继续执行++++++++++++++++++
0
2
4
6
8
10
12
14
16
18

Process finished with exit code 0

e. 异步+回调函数
#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"

from concurrent.futures import ThreadPoolExecutor
import threading
import time

def task(i):
print(“%s: 线程:%s 在运行%s 任务.” % (time.ctime(),threading.current_thread().name, i))
time.sleep(2)
return i * 2

parse 就是一个回调函数

def parse(future):
#处理每个线程的运行结果.
print(“%s: 线程结束了任务,相应的运行结果是:%s”%(threading.current_thread().name,future.result()))

pool = ThreadPoolExecutor(4)
for i in range(20):
#异步+回调函数.
future = pool.submit(task,i)
‘’’
给当前执行的任务绑定了一个函数,在当前任务结束的时候就会触发这个函数(称之为回调函数)
会把future对象作为参数传给函数
注:这个称为回调函数,当前任务处理结束了,就回来调parse这个函数
‘’’
future.add_done_callback(parse)

print(“++++++++++++++主线程继续执行++++++++++++++++++”)
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/test2.py”
Thu Feb 6 21:15:31 2020: 线程:Thread-1 在运行0 任务.
Thu Feb 6 21:15:31 2020: 线程:Thread-2 在运行1 任务.
Thu Feb 6 21:15:31 2020: 线程:Thread-3 在运行2 任务.
Thu Feb 6 21:15:31 2020: 线程:Thread-4 在运行3 任务.
++++++++++++++主线程继续执行++++++++++++++++++
Thread-1: 线程结束了任务,相应的运行结果是:0
Thread-2: 线程结束了任务,相应的运行结果是:2
Thu Feb 6 21:15:33 2020: 线程:Thread-1 在运行4 任务.
Thu Feb 6 21:15:33 2020: 线程:Thread-2 在运行5 任务.
Thread-4: 线程结束了任务,相应的运行结果是:6
Thread-3: 线程结束了任务,相应的运行结果是:4
Thu Feb 6 21:15:33 2020: 线程:Thread-4 在运行6 任务.
Thu Feb 6 21:15:33 2020: 线程:Thread-3 在运行7 任务.
Thread-2: 线程结束了任务,相应的运行结果是:10
Thread-1: 线程结束了任务,相应的运行结果是:8
Thu Feb 6 21:15:35 2020: 线程:Thread-2 在运行8 任务.
Thu Feb 6 21:15:35 2020: 线程:Thread-1 在运行9 任务.
Thread-4: 线程结束了任务,相应的运行结果是:12
Thu Feb 6 21:15:35 2020: 线程:Thread-4 在运行10 任务.
Thread-3: 线程结束了任务,相应的运行结果是:14
Thu Feb 6 21:15:35 2020: 线程:Thread-3 在运行11 任务.
Thread-1: 线程结束了任务,相应的运行结果是:18
Thu Feb 6 21:15:37 2020: 线程:Thread-1 在运行12 任务.
Thread-2: 线程结束了任务,相应的运行结果是:16
Thu Feb 6 21:15:37 2020: 线程:Thread-2 在运行13 任务.
Thread-3: 线程结束了任务,相应的运行结果是:22
Thu Feb 6 21:15:37 2020: 线程:Thread-3 在运行14 任务.
Thread-4: 线程结束了任务,相应的运行结果是:20
Thu Feb 6 21:15:37 2020: 线程:Thread-4 在运行15 任务.
Thread-2: 线程结束了任务,相应的运行结果是:26
Thread-1: 线程结束了任务,相应的运行结果是:24
Thu Feb 6 21:15:39 2020: 线程:Thread-2 在运行16 任务.
Thu Feb 6 21:15:39 2020: 线程:Thread-1 在运行17 任务.
Thread-4: 线程结束了任务,相应的运行结果是:30
Thread-3: 线程结束了任务,相应的运行结果是:28
Thu Feb 6 21:15:39 2020: 线程:Thread-3 在运行18 任务.
Thu Feb 6 21:15:39 2020: 线程:Thread-4 在运行19 任务.
Thread-1: 线程结束了任务,相应的运行结果是:34
Thread-2: 线程结束了任务,相应的运行结果是:32
Thread-3: 线程结束了任务,相应的运行结果是:36
Thread-4: 线程结束了任务,相应的运行结果是:38

Process finished with exit code 0

爬虫代码示例:

#!/usr/bin/env python3

-- coding:utf-8 --

from concurrent.futures import ThreadPoolExecutor
import threading
import requests

def handle(future):
response = future.result()
print(response.url,len(response.text),response.status_code)

def download(url):
“”"
:param url:
:return: 返回链接的下载结果,response包含了下载的所有内容.
“”"
response = requests.get(url)
return response

url_list = [
“http://www.sogou.com.com”,
“http://www.baidu.com”,
“https://www.taobao.com”,
]

pool = ThreadPoolExecutor(2)
for url in url_list:
future = pool.submit(download,url)
future.add_done_callback(handle)

print(“++++++++++++主线程++++++++++++++++++”)
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/Teacher.py”
++++++++++++主线程++++++++++++++++++
http://www.baidu.com/ 2381 200
https://www.taobao.com/ 133374 200
https://com.com/results?q=www.sogou 7107 200

Process finished with exit code 0

f. 线程池的创建方式
思考:直接创建线程的方式肯定比线程池的速度快吗?不一定,因为线程需要考虑cpu上下文切换的问题.

线程池创建的内部原理:最开始创建的时候线程池里面并没有线程,只有任务来的时候才会创建相应的线程.

#!/usr/bin/env python3

-- coding:utf-8 --

from concurrent.futures import ThreadPoolExecutor
import time
import threading

def task(*args, **kwargs):
time.sleep(2)
print(args, kwargs, time.ctime(), threading.current_thread().name)

pool = ThreadPoolExecutor(2)
for i in range(0, 10):
# 去连接池当中获取连接.
print(i)
pool.submit(task, i, i + 1)
“”"
相当于:
t = threading.Thread(target=task,args=(i,i+1,))
t.start()
“”"

print(“+++++++++++主线程++++++++++++”)
print(“+++++++++++主线程++++++++++++”)

运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/Teacher.py”
0
1
2
3
4
5
6
7
8
9
+++++++++++主线程++++++++++++
+++++++++++主线程++++++++++++
(0, 1) {} Wed Feb 5 22:00:14 2020 Thread-1
(1, 2) {} Wed Feb 5 22:00:14 2020 Thread-2
(2, 3) {} Wed Feb 5 22:00:16 2020 Thread-1
(3, 4) {} Wed Feb 5 22:00:16 2020 Thread-2
(5, 6) {} Wed Feb 5 22:00:18 2020 Thread-1
(4, 5) {} Wed Feb 5 22:00:18 2020 Thread-2
(6, 7) {} Wed Feb 5 22:00:20 2020 Thread-1
(7, 8) {} Wed Feb 5 22:00:20 2020 Thread-2
(9, 10) {} Wed Feb 5 22:00:22 2020 Thread-2
(8, 9) {} Wed Feb 5 22:00:22 2020 Thread-1

Process finished with exit code 0

应用示例1:爬取网页

#!/usr/bin/env python3

-- coding:utf-8 --

from concurrent.futures import ThreadPoolExecutor
import threading
import requests

def handle(future):
response = future.result()
“”"
response中封装了Http请求响应的所有数据
response.url 请求的URL
response.status_code 响应状态码
response.text 响应内容(字符串格式)
response.content 响应内容(字节格式)
“”"
print(response.url,response.status_code,len(response.text),len(response.content))

def download(url):
“”"
:param url:
:return: 返回链接的下载结果,response包含了下载的所有内容.
“”"
response = requests.get(url)
return response

url_list = [
“http://www.sogou.com.com”,
“http://www.baidu.com”,
“https://www.taobao.com”,
]

pool = ThreadPoolExecutor(2)
for url in url_list:
future = pool.submit(download,url)
future.add_done_callback(handle)

print(“++++++++++++主线程++++++++++++++++++”)

运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/Teacher.py”
++++++++++++主线程++++++++++++++++++
http://www.baidu.com/ 200 2381 2381
https://www.taobao.com/ 200 133374 135048
https://com.com/results?q=www.sogou 200 7107 7115

Process finished with exit code 0

e. 主线程等待线程池的线程执行完任务之后才继续执行 
#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"

from concurrent.futures import ThreadPoolExecutor
import threading
import os,time

def task(i):
print(“线程:%s 在运行当中…:%s”%(threading.current_thread().name,time.ctime()))
time.sleep(2)
return i * 2

pool = ThreadPoolExecutor(4)

res = []
for i in range(0,10):
future = pool.submit(task,i)
#异步提交方式.
res.append(future)

#主线程只有等待其余线程执行完毕之后,才会从无限时等待状态waiting转为可运行状态runnable
pool.shutdown(wait=True)

print(“++++++++++++++主线程继续执行++++++++++++++++++”)
for r in res:
print(r.result()) #获取每个线程对应的执行结果.
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/test2.py”
线程:Thread-1 在运行当中…:Thu Feb 6 20:31:11 2020
线程:Thread-2 在运行当中…:Thu Feb 6 20:31:11 2020
线程:Thread-3 在运行当中…:Thu Feb 6 20:31:11 2020
线程:Thread-4 在运行当中…:Thu Feb 6 20:31:11 2020
线程:Thread-1 在运行当中…:Thu Feb 6 20:31:13 2020
线程:Thread-2 在运行当中…:Thu Feb 6 20:31:13 2020
线程:Thread-3 在运行当中…:Thu Feb 6 20:31:13 2020
线程:Thread-4 在运行当中…:Thu Feb 6 20:31:13 2020
线程:Thread-3 在运行当中…:Thu Feb 6 20:31:15 2020
线程:Thread-2 在运行当中…:Thu Feb 6 20:31:15 2020
++++++++++++++主线程继续执行++++++++++++++++++
0
2
4
6
8
10
12
14
16
18

Process finished with exit code 0

f. 综合运用示例1
MyThread模块:

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"

from concurrent.futures import ThreadPoolExecutor
import threading
import time
import requests

def download(url):
response = requests.get(url)
return response

def run(url_list):
pool = ThreadPoolExecutor(2)
for item in url_list:
url = item[‘url’]
func = item[‘func’] #一旦拿到函数的引用,就可以调用这个函数了.
#异步+回调函数.
future = pool.submit(download,url)
future.add_done_callback(func)
启动模块:

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"

import MyThread

def handle(future):
response = future.result()
print(response.url,len(response.text),response.status_code)

url_list = [
{“url”:“http://www.sogou.com.com”,‘func’:handle},
{“url”:“http://www.baidu.com”,‘func’:handle},
{“url”:“https://www.taobao.com”,‘func’:handle}
]

MyThread.run(url_list)
运行结果:

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"

import MyThread

def handle(future):
response = future.result()
print(response.url,len(response.text),response.status_code)

url_list = [
{“url”:“http://www.sogou.com.com”,‘func’:handle},
{“url”:“http://www.baidu.com”,‘func’:handle},
{“url”:“https://www.taobao.com”,‘func’:handle}
]

MyThread.run(url_list)
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/test2.py”
http://www.baidu.com/ 2381 200
https://www.taobao.com/ 133374 200
https://com.com/results?q=www.sogou 7107 200

Process finished with exit code 0

g. 综合运用示例2
1、在urls.txt文件中包含了若干个图像url,一行一个url,请使用多线程下载这些图像文件,并按url出现的顺序保存为0.jpg、1.jpg、2.jpg

代码示例:

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"
from concurrent.futures import ThreadPoolExecutor
import requests
import time

url_list = []
with open(“urls.txt”,mode=“r”) as fr:
for line in fr.readlines():
if line.strip():
url_list.append(line.strip())

def download(url,name):
response = requests.get(url)
with open(name,mode=“wb”) as fw:
fw.write(response.content)

pool = ThreadPoolExecutor(2)
for index,url in enumerate(url_list):
pool.submit(download,url,“%s.jpg”%(index))

pool.shutdown(wait=True)

print(“+++++++++++++++++++++++++”)
url.txt:

https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1764209685,1216799602&fm=26&gp=0.jpg
https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1581007666242&di=8f1ecfe8adb80ea8e88d080d0b867e3c&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201508%2F12%2F20150812214120_UekJn.thumb.700_0.jpeg
https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1581604112916&di=32153f85208c92c8f65f42ec81daf17f&imgtype=0&src=http%3A%2F%2Fimg.pconline.com.cn%2Fimages%2Fupload%2Fupc%2Ftx%2Fitbbs%2F1310%2F07%2Fc45%2F27055241_1381140263580.jpg

(13)MyThread综合组件应用(重点)
网友开发的:

#!/usr/bin/python

-- coding:utf-8 --

import threading
from .MetaBaseRestrict import *

class MyThread(BaseRestrictClass,metaclass=MetaRestrictClass):

__doc__ = '线程工具'


def __init__(self, func_list=None):
#所有线程函数的返回值汇总,用来判断线程是否执行异常
    self.ret_flag = 0
    self.func_list = func_list


def set_thread_func_list(self, func_list):
    """
    @note: func_list是一个list,每个元素是一个dict,有func和args两个参数
    func_list:[{"func":self.add_tableinfos,"args":(mart_table,)},{"func":self.add_tableinfos,"args":(mart_table,)}]
    """
    self.func_list = func_list


def start(self):
    """
    @note: 启动多线程执行,并阻塞到结束
    """
    self.ret_flag = 0
    self.threads = []
    for func_dict in self.func_list:
        if func_dict["args"]:
            new_arg_list = []
            new_arg_list.append(func_dict["func"])
            for arg in func_dict["args"]:
                new_arg_list.append(arg)
            new_arg_tuple = tuple(new_arg_list)
            t = threading.Thread(target=self.trace_func, args=new_arg_tuple)
        else:
            t = threading.Thread(target=self.trace_func, args=(func_dict["func"],))
        self.threads.append(t)

    for thread_obj in self.threads:
        thread_obj.start()

    for thread_obj in self.threads:
        thread_obj.join()

def ret_value(self):
    """
    @note: 所有线程函数的返回值之和
    """
    return self.ret_flag


def trace_func(self, func, *args, **kwargs):
    """
    @note:替代profile_func,新的跟踪线程返回值的函数,对真正执行的线程函数包一次函数,以获取返回值
    """
    func(*args, **kwargs)
    self.ret_flag += 1

组件1:创建多个线程执行任务,同时可以判断出所有的线程是否执行成功,同时主线程需要等待所有子线程运行完毕后才会继续执行.
缺点:如果开1万个任务,就会开启1万个线程,机器可能挂掉.

#!/usr/bin/python

-- coding:utf-8 --

import threading

class MyThread(object):

__doc__ = '线程工具:多线程并行执行,同时判断出多个线程是否全部执行成功.'

def __init__(self, func_list):
    """
    :param func_list: @note: func_list是一个list,每个元素是一个dict,有func和args两个参数
    """
    #所有线程执行成功个数的汇总值,初始值是0个.
    self.ret_flag = 0
    # 创建一个互斥锁.
    self.lock = threading.Lock()
    self.func_list = func_list


def start(self):
    """
    @note: 启动多线程执行,并阻塞到结束
    """
    thread_list = []

    for func_dict in self.func_list:
        func = func_dict["func"]
        args = func_dict["args"]
        t = threading.Thread(target=self.trace_func,args=(func,args))
        t.start()
        thread_list.append(t)

    for thread_obj in thread_list:
        thread_obj.join()


def trace_func(self, func, args):
    """
    :param func: 用户真正的线程函数.
    :param args: 线程参数.
    :return: 
    """
    func(*args)
    #当前线程执行成功的话,标记数+1
    self.lock.acquire()
    self.ret_flag += 1
    self.lock.release()

应用示例1:

#!/usr/bin/env python3

-- coding:utf-8 --

from MyThread import MyThread
import traceback
import time

#线程函数.
def func(m):
try:
print(1/m)
time.sleep(2)
except Exception as e:
#注意:这个地方必须用traceback,如果没用的话,某个线程即使执行时出现异常,返回标志依然是成功.
traceback.print_exc(str(e))

func_list = []

for i in range(0,10):
t = {“func”:func,“args”:(i,)}
func_list.append(t)

mt = MyThread(func_list)
mt.start()

if (mt.ret_flag) == len(func_list):
print(“全部线程:%s 全部执行成功!”%(len(func_list)))
else:
print(“全部线程:%s 执行成功:%s 个,失败:%s 个.”%(len(func_list),mt.ret_flag,len(func_list)-mt.ret_flag))

print(“+++++++++++++主线程++++++++++++++”)
print(“+++++++++++++主线程++++++++++++++”)
print(“+++++++++++++主线程++++++++++++++”)
print(“+++++++++++++主线程++++++++++++++”)
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/test.py”
1.0
Traceback (most recent call last):
0.5
0.3333333333333333
0.25
0.2
0.16666666666666666
0.14285714285714285
0.125
0.1111111111111111
Exception in thread Thread-1:
Traceback (most recent call last):
File “D:/Python Work Location/DBS_NEW_@/DBS_NEW/test.py”, line 12, in func
print(1/m)
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “D:\Python34\lib\threading.py”, line 911, in _bootstrap_inner
self.run()
File “D:\Python34\lib\threading.py”, line 859, in run
self._target(*self.args, **self.kwargs)
File "D:\Python Work Location\DBS_NEW
@\DBS_NEW\MyThread.py", line 46, in trace_func
func(*args)
File "D:/Python Work Location/DBS_NEW
@/DBS_NEW/test.py", line 16, in func
traceback.print_exc(str(e))
File “D:\Python34\lib\traceback.py”, line 252, in print_exc
print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain)
File “D:\Python34\lib\traceback.py”, line 169, in print_exception
for line in _format_exception_iter(etype, value, tb, limit, chain):
File “D:\Python34\lib\traceback.py”, line 153, in _format_exception_iter
yield from _format_list_iter(_extract_tb_iter(tb, limit=limit))
File “D:\Python34\lib\traceback.py”, line 18, in _format_list_iter
for filename, lineno, name, line in extracted_list:
File “D:\Python34\lib\traceback.py”, line 58, in _extract_tb_or_stack_iter
while curr is not None and (limit is None or n < limit):
TypeError: unorderable types: int() < str()

全部线程:10 执行成功:9 个,失败:1 个.
+++++++++++++主线程++++++++++++++
+++++++++++++主线程++++++++++++++
+++++++++++++主线程++++++++++++++
+++++++++++++主线程++++++++++++++

Process finished with exit code 0

应用示例2:

#!/usr/bin/env python3

-- coding:utf-8 --

from MyThread import MyThread
import traceback
import time

class Obj_Thread(object):

# 线程函数.
def func(self,m):
    try:
        print(1 / m)
        time.sleep(2)
    except Exception as e:
        # 注意:这个地方必须用traceback,如果没用的话,某个线程即使执行时出现异常,返回标志依然是成功.
        traceback.print_exc(str(e))


def run(self):
    func_list = []
    for i in range(0, 10):
        t = {"func": self.func, "args": (i,)}
        func_list.append(t)
    mt = MyThread(func_list)
    mt.start()

    if (mt.ret_flag) == len(func_list):
        print("全部线程:%s 全部执行成功!" % (len(func_list)))
    else:
        print("全部线程:%s 执行成功:%s 个,失败:%s 个." % (len(func_list), mt.ret_flag, len(func_list) - mt.ret_flag))

if name == ‘main’:
obj = Obj_Thread()
obj.run()
运行结果:同上.

组件2:通过线程池并行执行任务,同时可以判断出所有的线程是否执行成功,同时主线程需要等待所有子线程运行完毕后才会继续执行.
#!/usr/bin/python

-- coding:utf-8 --

import threading
from concurrent.futures import ThreadPoolExecutor

class MyThread(object):

__doc__ = '线程工具:多线程并行执行,同时判断出多个线程是否全部执行成功.'

def __init__(self, func_list,thread_workers):
    """
    :param func_list: @note: func_list是一个list,每个元素是一个dict,有func和args两个参数
    :param max_workers:  线程池的工作个数.
    """
    #所有线程执行成功个数的汇总值,初始值是0个.
    self.ret_flag = 0
    # 自定义一个锁.
    self.lock = threading.Lock()
    # 自定义一个线程池.
    self.pool = ThreadPoolExecutor(thread_workers)
    self.func_list = func_list

	
def start(self):
    """
    @note: 启动多线程执行,并阻塞到结束
    """

    for func_dict in self.func_list:
        func = func_dict["func"]
        args = func_dict["args"]
        #放入线程池,开始执行任务.
        self.pool.submit(self.trace_func,func,args)

    #主线程只有等待线程池所有任务都执行完之后,才会从无限时等待状态waiting转为可运行状态runnable
    self.pool.shutdown(wait=True)


def trace_func(self, func, args):
    """
    :param func: 用户真正的线程函数.
    :param args: 线程参数.
    :return: 
    """
    func(*args)
    #当前线程执行成功的话,标记数+1,一旦某个线程执行的过程当中出现异常,下面的代码就不会执行.
    self.lock.acquire()
    self.ret_flag += 1
    self.lock.release()

应用实例1:

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"
import time
import threading
from concurrent.futures import ThreadPoolExecutor
import traceback
from MyThread import MyThread

线程函数.

def func(m):
try:
print(1 / m)
time.sleep(2)
except Exception as e:
# 注意:这个地方必须用traceback,如果没用的话,某个线程即使执行时出现异常,返回标志依然是成功.
traceback.print_exc(str(e))

func_list = []

for i in range(0, 10):
t = {“func”: func, “args”: (i,)}
func_list.append(t)

mt = MyThread(func_list,thread_workers=4)
mt.start()

if (mt.ret_flag) == len(func_list):
print(“全部线程:%s 全部执行成功!” % (len(func_list)))
else:
print(“全部线程:%s 执行成功:%s 个,失败:%s 个.” % (len(func_list), mt.ret_flag, len(func_list) - mt.ret_flag))
运行结果:

D:\Python34\python.exe “D:/Python Work Location/DBS_NEW_@/DBS_NEW/test2.py”
1.0
0.5
0.3333333333333333
0.25
Traceback (most recent call last):
0.2
0.16666666666666666
0.14285714285714285
0.125
0.1111111111111111
全部线程:10 执行成功:9 个,失败:1 个.

Process finished with exit code 0
实例2:

#!/usr/bin/env python3

-- coding:utf-8 --

“”"
author:zhang ming yang
“”"
import time
import threading
from concurrent.futures import ThreadPoolExecutor
import traceback
from MyThread import MyThread

class Obj_Thread(object):

# 线程函数.
def func(self,m):
    try:
        print(1 / m)
        time.sleep(2)
    except Exception as e:
        print(str(e))
        # 注意:这个地方必须用traceback,如果没用的话,某个线程即使执行时出现异常,返回标志依然是成功.
        traceback.print_exc(str(e))

def run(self):
    func_list = []

    for i in range(0, 10):
        t = {"func": self.func, "args": (i,)}
        func_list.append(t)

    mt = MyThread(func_list,thread_workers=4)
    mt.start()

    if (mt.ret_flag) == len(func_list):
        print("全部线程:%s 全部执行成功!" % (len(func_list)))
    else:
        print("全部线程:%s 执行成功:%s 个,失败:%s 个." % (len(func_list), mt.ret_flag, len(func_list) - mt.ret_flag))

if name == ‘main’:
obj = Obj_Thread()
obj.run()
运行结果:同上。

(14)GIL全局解释器锁
GIL,全局解释器锁:

  • 保证同一个进程中只有一个线程同时被CPU调度.

简单的总结下就是:Python的多线程在多核CPU上,只对于IO密集型计算产生正面效果;而当有至少有一个CPU密集型线程存在,那么多线程效率会由于GIL而大幅下降。

参考博客:

https://www.jianshu.com/p/9eb586b64bdb

https://www.cnblogs.com/cjaaron/p/9166538.html

应用场景:
IO密集型:线程
计算密集型:进程

对比图:

Java、C#等语言:同一个进程当中的多个线程可以同时被多个CPU调度,可以利用多核的优势;

Python:同一个进程当中只有1个线程可以被CPU调度,不能利用多核的优势;

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只懒得睁眼的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值