- 程序:一堆代码以文本形式存入一个文档
- 进程: 程序运行的一个状态
- 包含地址空间,内存,数据栈等
- 每个进程由自己完全独立的运行环境,多进程共享数据是一个问题
- 线程
- 一个进程的独立运行片段,一个进程可以由多个线程
- 轻量化的进程
- 一个进程的多个线程间共享数据和上下文运行环境
- 共享互斥问题
- 全局解释器锁(GIL)
- Python代码的执行是由python虚拟机进行控制
- 在主循环中只能有一个控制线程在执行
- 参考文档:https://www.cnblogs.com/cjaaron/p/9166538.html
概念
- 同步和异步关注的是
消息通信机制
(synchronous communication/ asynchronous communication).- 所谓
同步
,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了.- 所谓
异步
,则是调用在发出之后,这个调用就直接返回了,所以没有返回结果。后续被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用.- 参考文档:https://www.cnblogs.com/mhq-martin/p/9035640.html
- 并发和并行:https://blog.csdn.net/weixin_39003229/article/details/80681024
thread:有问题,不好用
_thread:python3改成了_thread
threading: 通行的包
# _thread多线程,缩短总时间
import time
import _thread as thread
def loop1(in1):
# ctime 得到当前时间
print('Start loop 1 at :', time.ctime())
# 把参数打印出来
print("我是参数 ",in1)
# 睡眠多长时间,单位是秒
time.sleep(4)
print('End loop 1 at:', time.ctime())
def loop2(in1, in2):
# ctime 得到当前时间
print('Start loop 2 at :', time.ctime())
# 把参数in 和 in2打印出来,代表使用
print("我是参数 " ,in1 , "和参数 ", in2)
# 睡眠多长时间,单位是秒
time.sleep(2)
print('End loop 2 at:', time.ctime())
def main():
print("Starting at:", time.ctime())
# 参数两个,一个是需要运行的函数名,第二是函数的参数作为元祖使用,为空则使用空元祖
# 注意:如果函数只有一个参数,需要参数后由一个逗号
thread.start_new_thread(loop1,("王老大", ))
thread.start_new_thread(loop2,("王大鹏", "王晓鹏"))
print("All done at:", time.ctime())
if __name__ == "__main__":
main()
# 一定要有while语句
# 因为启动多线程后本程序就作为主线程存在,如果主线程执行完毕,则子线程可能也需要终止
while True:
time.sleep(10)
>Starting at: Thu Aug 23 21:52:50 2018
All done at: Wed Aug 22 22:24:40 2018
Start loop 1 at : Wed Aug 22 22:24:40 2018
我是参数 王老大
Start loop 2 at : Wed Aug 22 22:24:40 2018
我是参数 王大鹏 和参数 王晓鹏
End loop 2 at: Wed Aug 22 22:24:42 2018
End loop 1 at: Wed Aug 22 22:24:44 2018
直接利用threading.Thread生成Thread实例
语法:
t = threading.Thread(target=xxx, args=(xxx,))
t.start():启动多线程
t.join(): 主线程等待多线程执行完成
无join()方法时,子线程启动后,程序会将资源返回给主程序。
import threading
import time
def func1():
time.sleep(2)
print(func1)
time.sleep(2)
print(func1)
def func2():
time.sleep(3)
print(func2)
if __name__ == "__main__":
t1 = threading.Thread(target=func1)
t2 = threading.Thread(target=func2)
t1.start()
t2.start()
# t1.join()
# t2.join()
print(threading.activeCount())
print('运行结束')
print(threading.activeCount())
print(threading.activeCount())
>
3
运行结束
3
3
<function func1 at 0x7ffb59db9160>
<function func2 at 0x7ffb5aae2670>
join():阻塞当前进程/线程,直到调用join方法的那个进程执行完,再继续执行当前进程
if __name__ == "__main__":
t1 = threading.Thread(target=func1)
t2 = threading.Thread(target=func2)
t1.start()
t2.start()
t1.join()
t2.join()
print(threading.activeCount())
print('运行结束')
print(threading.activeCount())
print(threading.activeCount())
>
<function func1 at 0x7fdacf6228b0>
<function func2 at 0x7fdacf6d8700>
<function func1 at 0x7fdacf6228b0>
1
运行结束
1
1
在程序中将子线程设置成守护现成,则子线程会在主线程结束的时候自动退出
一般认为,守护线程不重要或者不允许离开主线程独立运行
守护线程案例能否有效果跟环境相关
# 非守护线程
import time as t
import threading
def fun():
print('start fun')
t.sleep(2)
print('end fun')
print('main thread')
## 注意:此处函数不带括号
t1 = threading.Thread(target=fun, args=())
t1.start()
t.sleep(1)
print('main thread end')
>main thread
start fun
main thread end
end fun
## 从结果看出:主程序结束后,线程并未结束
# 守护线程
import time
import threading
def fun():
print("Start fun")
time.sleep(2)
print("end fun")
print("Main thread")
t1 = threading.Thread(target=fun, args=() )
# 社会守护线程的方法,必须在start之前设置,否则无效
t1.setDaemon(True)
# 设置线程的第二种方式
#t1.daemon = True
t1.start()
time.sleep(1)
print("Main thread end")
>Main thread
Start fun
Main thread end
# 主线程结束后,守护线程也结束
threading.currentThread:返回当前线程变量
threading.enumerate:返回一个包含正在运行的线程的list,正在运行的线程指的是线程启动后,结束前的状态
threading.activeCount: 返回正在运行的线程数量,效果跟 len(threading.enumerate)相同
threading.setName: 给线程设置名字
threading.getName: 得到线程的名字
import time
import threading
def loop1():
print('Start loop 1 at :', time.ctime())
time.sleep(6)
print('End loop 1 at:', time.ctime())
def loop2():
print('Start loop 2 at :', time.ctime())
time.sleep(1)
print('End loop 2 at:', time.ctime())
def loop3():
print('Start loop 3 at :', time.ctime())
time.sleep(5)
print('End loop 3 at:', time.ctime())
def main():
print("Starting at:", time.ctime())
t1 = threading.Thread(target=loop1, args=( ))
# threading.setName: 给线程设置名字
t1.setName("THR_1")
t1.start()
t2 = threading.Thread(target=loop2, args=( ))
t2.setName("THR_2")
t2.start()
t3 = threading.Thread(target=loop3, args=( ))
t3.setName("THR_3")
t3.start()
# 3秒后,thread2已经自动结束,
time.sleep(3)
# enumerate 得到正在运行子线程,即子线程1和子线程3
for thr in threading.enumerate():
# getName能够得到线程的名字
print("正在运行的线程名字是: {0}".format(thr.getName()))
# threading.activeCount: 返回正在运行的线程数量,效果跟 len(threading.enumerate)相同
print("正在运行的子线程数量为:{0}".format(threading.activeCount()))
print("All done at:", time.ctime())
if __name__ == "__main__":
main()
# 一定要有while语句
# 因为启动多线程后本程序就作为主线程存在 # 如果主线程执行完毕,则子线程可能也需要终止
while True:
time.sleep(10)
>Starting at: Thu Aug 23 22:19:20 2018
Start loop 1 at : Thu Aug 23 22:19:20 2018
Start loop 2 at : Thu Aug 23 22:19:20 2018
Start loop 3 at : Thu Aug 23 22:19:20 2018
End loop 2 at: Thu Aug 23 22:19:21 2018
正在运行的线程名字是: MainThread
正在运行的线程名字是: THR_1
正在运行的线程名字是: THR_3
正在运行的子线程数量为:3
All done at: Thu Aug 23 22:19:23 2018
End loop 3 at: Thu Aug 23 22:19:25 2018
End loop 1 at: Thu Aug 23 22:19:26 2018
以上均为直接生成Thread的实例,现在采用第二种直接继承自threading.Thread
语法:
- 直接继承Thread
- 重写run函数
- 类实例可以直接运行
import threading
import time
# 1. 类需要继承自threading.Thread
class MyThread(threading.Thread):
def __init__(self, arg):
super(MyThread, self).__init__()
self.arg = arg
# 2 必须重写run函数,run函数代表的是真正执行的功能
def run(self):
time.sleep(2)
print("The args for this class is {0}".format(self.arg))
for i in range(5):
t = MyThread(i)
t.start()
t.join()
print("Main thread is done!!!!!!!!")
>The args for this class is 0
The args for this class is 1
The args for this class is 2
The args for this class is 3
The args for this class is 4
Main thread is done!!!!!!!!
企业多线程写法
import threading
from time import sleep, ctime
class ThreadFunc:
def __init__(self, name):
self.name = name
def loop(self, nloop, nsec):
"""
:param nloop: loop函数的名称
:param nsec: 系统休眠时间
:return:
"""
print('Start loop ', nloop, 'at ', ctime())
sleep(nsec)
print('Done loop ', nloop, ' at ', ctime())
def main():
print("Starting at: ", ctime())
# 以下t1 和 t2的定义方式相等
t = ThreadFunc("loop")
t1 = threading.Thread(target=t.loop, args=("LOOP1", 4))
# 下面这种写法更西方人,工业化一点
t2 = threading.Thread(target=ThreadFunc('loop').loop, args=("LOOP2", 2))
t1.start()
t2.start()
t1.join()
t2.join()
print("ALL done at: ", ctime())
if __name__ == '__main__':
main()
>Starting at: Thu Aug 23 22:55:49 2018
Start loop LOOP1 at Thu Aug 23 22:55:49 2018
Start loop LOOP2 at Thu Aug 23 22:55:49 2018
Done loop LOOP2 at Thu Aug 23 22:55:51 2018
Done loop LOOP1 at Thu Aug 23 22:55:53 2018
ALL done at: Thu Aug 23 22:55:53 2018
共享变量: 当多个线程同时访问全局变量的时候,可能造成多线程之间对全局变量的混乱(即线程非安全)
import threading
sum = 0
n = 1000000
def Add():
global sum, n
for i in range(n):
sum += 1
def Minu():
global sum, n
for i in range(n):
sum -= 1
if __name__ == '__main__':
print('Starting......{0}'.format(sum))
t1 = threading.Thread(target=Add, args=())
t2 = threading.Thread(target=Minu, args=())
t1.start()
t2.start()
t1.join()
t2.join()
print('End......{0}'.format(sum))
>Starting......0
End......-182314
共享变量问题解决方法:锁(Lock)—是一个标志,表示一个线程在占用一些资源
步骤:上锁–>使用共享资源–>取消锁,释放锁
import threading
sum = 0
n = 1000000
lock =threading.Lock()
def Add():
global sum, n
for i in range(n):
## 上锁,申请锁
lock.acquire()
sum += 1
## 释放锁
lock.release()
def Minu():
global sum, n
for i in range(n):
lock.acquire()
sum -= 1
lock.release()
if __name__ == '__main__':
print('Starting......{0}'.format(sum))
t1 = threading.Thread(target=Add, args=())
t2 = threading.Thread(target=Minu, args=())
t1.start()
t2.start()
t1.join()
t2.join()
print('End......{0}'.format(sum))
>Starting......0
End......0
当存在多把锁时,容易造成死锁问题,因此使用python锁时需要谨慎
import threading
import time
lock_1 = threading.Lock()
lock_2 = threading.Lock()
def func_1():
print("func_1 starting.........")
lock_1.acquire()
print("func_1 申请了 lock_1....")
time.sleep(2)
print("func_1 等待 lock_2.......")
lock_2.acquire()
print("func_1 申请了 lock_2.......")
lock_2.release()
lock_1.release()
def func_2():
print("func_2 starting.........")
lock_2.acquire()
print("func_2 申请了 lock_2....")
time.sleep(4)
print("func_2 等待 lock_1.......")
lock_1.acquire()
print("func_2 申请了 lock_1.......")
lock_1.release()
lock_2.release()
if __name__ == "__main__":
print("主程序启动..............")
t1 = threading.Thread(target=func_1, args=())
t2 = threading.Thread(target=func_2, args=())
t1.start()
t2.start()
t1.join()
t2.join()
>主程序启动......
函数1没有申请到锁2,会释放锁1
函数2申请到了锁1
主程序完成......
- 一个锁,可以被一个线程多次申请
- 主要解决递归调用的时候,需要申请锁的情况
import threading
import time
class MyThread(threading.Thread):
## 重写run函数
def run(self):
global num
time.sleep(1)
if mutex.acquire(1):
num = num+1
msg = self.name +' set num to '+str(num)
print(msg)
mutex.acquire()
mutex.release()
mutex.release()
num = 0
## 实例化可重入锁
mutex = threading.RLock()
def testTh():
for i in range(5):
t = MyThread()
t.start()
设置一个资源最多由几个多线程同时使用,超出的新线程需要等待资源
import threading
import time
# 参数定义最多几个线程同时使用资源
semaphore = threading.Semaphore(3)
def func():
if semaphore.acquire():
print(threading.currentThread().getName() + ' get semaphore')
time.sleep(2)
semaphore.release()
print(threading.currentThread().getName() + ' release semaphore')
for i in range(8):
t1 = threading.Thread(target=func)
t1.start()
threading.Timer–利用多线程,在指定时间后启动一个功能
import threading
import time
def func():
print("I am running.........")
time.sleep(4)
print("I am done......")
if __name__ == "__main__":
t = threading.Timer(6, func)
t.start()
i = 0
while True:
print("{0}***************".format(i))
time.sleep(3)
i += 1
- python语言和GIL半毛钱关系都没有,是由于Cpython解释器的历史原因导致。
- GIL:全局解释器锁。每个线程在执行的过程中都要先获取GIL,保证同时只有一个线程可以执行代码。因此python多线程只能使用一个CPU,无法有效利用多核CPU资源
- 特例:python写文件时由于不需要python解释器的支持,因此可以实现并发。
- IO密集型:涉及到大量网络、磁盘IO的任务,特点是CPU消耗低,大量时间浪费在IO等待,建议使用多线程
- CPU密集型:涉及到大量计算的任务,特点是CPU消耗高,不建议使用多线程