相关概念
- 程序:写好的代码,保存在硬盘里
- 进程:进行中的程序,在内存中运行,相当于把硬盘中的程序挪到内存中,并执行。
- 线程:一个进程中独立运行的片段,同一个进程可以有多个线程,共享同一个进程的数据和上下文运行环境
- 全局解释器锁:在python主循环中只能有一个控制线程在执行
_thread包
- 此包在python3中尽量不用
- thread:有问题不用,python3中已经改成_thread
- _thread.start_new_thread(函数名, 参数元组)
#_thread 例子
#问题1:主线程提前结束,导致分线程不一定能正常结束
#问题2:分线程运行顺序不定
import time
import _thread as thr
def loop1():
print("loop1开始工作:", time.ctime())
time.sleep(4)
print("loop1结束工作:", time.ctime())
def loop2():
print("loop2开始工作:", time.ctime())
time.sleep(2)
print("loop2结束工作:", time.ctime())
def main():
print("主程序开始工作:", time.ctime())
thr.start_new_thread(loop1, ())
thr.start_new_thread(loop2, ())
print("主程序结束工作:", time.ctime())
if __name__ == "__main__":
main()
threading包
-
在python3中,多数在用此包
-
threading.Thread(target=函数名, args=参数元组) 实例化线程
-
threading.Timer(3.0, 函数名,[参数]) 指定时间后执行函数
-
.start() 启动线程
-
.join() 等待子线程执行完毕
#threading 例子 import time import threadingr def loop1(): print("loop1开始工作:", time.ctime()) time.sleep(4) print("loop1结束工作:", time.ctime()) def loop2(): print("loop2开始工作:", time.ctime()) time.sleep(2) print("loop2结束工作:", time.ctime()) def main(): print("主程序开始工作:", time.ctime()) #第一个实例线程 l1 = threading.Thread(target=loop1, args=()) l1.start() #第二个实例线程 l2 = threading.Thread(target=loop2, args=()) l2.start() #主程序等待两个线程 l1.join() l2.join() print("主程序结束工作:", time.ctime()) if __name__ == "__main__": main()
-
.setDaemon(True)或.deamon=True 守护线程
- 守护线程会在主线程结束时一起结束(和运行环境有关,不一定实现)
- 在启动线程前进行设置
#线程守护例子 import time import threading def loop1(): print("子线程开始") time.sleep(2) print("子线程结束") def main(): print("主线程开始") l = threading.Thread(target=loop1, args=()) l.setDaemon(True) l.start() print("主线程结束") if __name__ == "__main__": main()
-
threading常用属性
- threading.currentThread() : 返回当前运行中线程变量(整体线程元素)
- threading.enumerate() : 返回当前活动线程的数量,已列表形式
- threading.activeCount() : 返回当前活动线程的数量,相当于len(threading.enumerate)
- threading.setName() : 给线程设置名字
- threading.getName() : 返回线程的名字
import time import threading def loop1(): print("线程1开始") time.sleep(2) print("线程1结束") def loop2(): print("线程2开始") time.sleep(4) print("线程2结束") def loop3(): print("线程3开始") time.sleep(6) print("线程3结束") def main(): print("主线程开始") l1 = threading.Thread(target=loop1, args=()) l1.setName("l1") l1.start() l2 = threading.Thread(target=loop2, args=()) l2.setName("l2") l2.start() l3 = threading.Thread(target=loop3, args=()) l3.setName("l3") l3.start() time.sleep(1) print("当前运行线程的参数的变量名:", threading.currentThread()) print("当前活动线程数量:", threading.activeCount()) for i in threading.enumerate(): print(i.getName()) print("主线程结束") if __name__ == "__main__": main()
另一种方式
-
threading.Thread子类方式
-
子类可不写__init__函数,但是如果写,就一定要调用父类的__init__函数
-
子类必须重新构run函数,run函数是执行内容,就是target参数所给函数的内容 .start()所要执行的内容
import time import threading class T(threading.Thread): def __init__(self, arg): self.arg = arg #python2中用super(T, self).__init__() super().__init__() def run(self): time.sleep(2) print(self.arg) for i in range(5): l = T(i) l.start() l.join() print("主线程结束")
共享变量
多个线程同时操作一个变量
- 由于操作顺序不定,导致结果不同
- 解决方法:threading.lock()
import threading
import time
s = 0
ls = 100000
tlock = threading.Lock()
def jia():
#global把局部变量变为全局变量
global s, ls
time.sleep(1)
for i in range(1, ls):
tlock.acquire()
s += 1
tlock.release()
def jian():
global s, ls
time.sleep(1)
for i in range(1, ls):
tlock.acquire()
s -= 1
tlock.release()
if __name__ == "__main__":
j1 = threading.Thread(target=jia, args=())
j2 = threading.Thread(target=jian, args=())
j1.start()
j2.start()
j1.join()
j2.join()
print(s)
死锁问题
-
解决办法threading.acquire(timeout=4) 超过四秒就不在申请
- 若申请不到,就不能释放,此时做if处理
-
treading.semphore(3) 同时允许3个进程使用某变量
import threading import time sem = threading.Semaphore(3) def func(): if sem.acquire(): print(threading.currentThread().getName() + "获得线程锁") time.sleep(10) sem.release() print(threading.currentThread().getName() + "释放线程所") for i in range(8): t = threading.Thread(target=func, args=()) t.start()
可重入锁问题
-
同一个线程多次申请锁的问题
-
一般用于递归函数
-
threading.RLock()
import threading import time class xiancheng(threading.Thread): def run(self): global a time.sleep(1) if loc.acquire(timeout=1): a = a + 1 s = self.name + "设置数" + str(a) print(s) loc.acquire() loc.release() loc.release() def tx(): for i in range(5): l = xiancheng() l.start() if __name__ == "__main__": a = 0 loc = threading.RLock()#实例可重入锁 tx()
变量安全
- 不安全变量类型:list, set, dict等,可改变的变量类型
- 安全变量:queue队列
- queue.put(数据) 存入数据
- queue.get() 去除数据
import queue
import time
import threading
class shengchanzhe(threading.Thread):
def run(self):
global q
a = 0
while True:
if a < 1000:
for i in range(100):
a = a + 1
b = "产品:" + str(a)
q.put(b)
print(b)
time.sleep(0.5)
class xiaofeizhe(threading.Thread):
def run(self):
global q
while True:
if q.qsize() > 100:
for i in range(5):
#线程有个.name属性,即线程名,可用.setName()和.getName()修改
b = self.name + "消费了" + q.get()
print(b)
time.sleep(1)
if __name__ == "__main__":
q = queue.Queue()
for i in range(3):
s = shengchanzhe()
s.start()
for i in range(5):
x = xiaofeizhe()
x.start()
线程的替代方案
- subprocess
- 完全跳过线程,使用进程
- 派生进程的主要替代方式
- python2.4后引入
- multiprocessiong
- 使用threading接口派生,使用子进程
- 允许多核或多cpu派生进程,接口和threading极其相似
- python2.6后引入
- concurrent.futures
- 新的异步执行模块
- 任务级别的操作
- python3.2后引入