python 多线程 threading
一、多线程使用的的基本方式
1、使用threading中现成的方法
import threading def f1(arg): print(arg) t1 = threading.Thread(target=f1, args=(123,)) t2 = threading.Thread(target=f1, args=(456,)) t1.start() t2.start()
说明:1、实例化一个Thead类就是生成一个线程。
2、target是要执行的函数,args是函数的参数,以元组的形式提现。
3、start函数代表线程已经准备好,等待CPU的调用。
2、使用自定义的多线程类
import threading class MyThread(threading.Thread): def __init__(self, func, arg): self._func = func self._arg = arg super(MyThread, self).__init__() def run(self): self._func(self._arg) t1 = MyThread(f2, 123) t.start() t2 = MyThread(f2, 456) t.start()
说明:1、该类继承自threading.Thread类。
2、自定义线程类需要重写run方法,run方法中就是该线程要做的事情。
3、 如果需要重写构造方法__init__, 需要继承父类的构造方法调用super函数。
4、start方法同上
3、守护线程
1)守护线程即守护主线程的线程,与主线程同生共死。
2)主线程退出时,守护线程也跟着退出。
3)默认情况下,创建的子线程非守护线程,既主线程如果执行完毕的,而子线程没有执行完毕,主线程会等待子线程执行完毕后再退出。
4)设置守护线程的方法是 setDaemon = True。
import threading import time def f1(arg): time.sleep(5) print(arg) t = threading.Thread(target=f1, args('我是守护线程')) t.setDaemon = True t.start() print('end')
说明: 该程序中子线程设置为守护线程,主线程退出后,子线程也跟着退出(没有打印‘我是守护线程’)。
4. join方法
join方法会是主线程阻塞,直到使用join方法的子线程执行完毕后,继续往下执行。
join方法还有个参数,设置阻塞的时间,默认情况下会无限阻塞下去。
import threading import time def f1(arg): time.sleep(5) print(arg) t = threading.Thread(target=f1, args('123')) t.setDaemon = True t.join() print('end')
说明:该程序会在执行到join方法的时候阻塞住,直到打印出123,再继续往下执行。
5. 线程中的其他方法
setName 为某个线程设置名字
getName 获取某个线程的名字
二、线程锁
1.Lock
由于线程使用相同的内存空间,可能在同一时间内去修改同一数据,对数据造成破坏,这种情况称作"线程不安全"。
import threading import time class MyThread(threading.Thread): def run(self): global num time.sleep(1) num = num+1 msg = self.name+' set num to '+str(num) print msg num = 0 def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
但是运行结果是不正确的
Thread-5 set num to 2 Thread-3 set num to 3 Thread-2 set num to 5 Thread-1 set num to 5 Thread-4 set num to 4
问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。既“线程不安全”。
上面的程序引出了多线程编程的最常见问题:数据共享。当多个线程都修改某一个共享数据的时候,需要进行同步控制。
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
import threading import time class MyThread(threading.Thread): def run(self): global num time.sleep(1) if Lock.acquire(1): num = num+1 msg = self.name+' set num to '+str(num) print msg Lock.release() num = 0 Lock = threading.Lock() def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
运行结果: Thread-3 set num to 1 Thread-4 set num to 2 Thread-5 set num to 3 Thread-2 set num to 4 Thread-1 set num to 5
可以看出使用了线程锁的程序结果复合预期效果。
2、RLock
Lock只能对某个线程锁一次,而RLock可以锁多次。使用方法同Lock
3、信号量 Semaphore
互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 。
import threading import time n = 10 def func(l, i): global n l.acquire() n -= 1 time.sleep(1) print(n, i) l.release() lock = threading.BoundedSemaphore(5) for i in range(10): t = threading.Thread(target=func, args=(lock, i)) t.start()
输出结果: 5 0 4 1 3 2 2 4 2 3 第一次(秒)输出结果:5个 ------------------------- 0 5 第二次(秒)输出结果:5个 0 6 0 7 0 8 0 9
4、事件 event
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
- clear:将“Flag”设置为False
- set:将“Flag”设置为True
#-*-coding:utf8-*- import threading def func(i,e): print(i) e.wait() # 检测是什么等,如果是红灯,停;绿灯,行 print(i+100) event = threading.Event() for i in range(10): t = threading.Thread(target=func, args=(i,event,)) t.start() #======== event.clear() # 设置成红灯 inp = input('>>>') if inp == "1": event.set() # 设置成绿灯
输出结果: 0 1 2 3 4 5 6 7 8 9 >>>1 # 在此处阻塞住,输入数字1,将Flag设置成True 101 100 103 105 107 109 102 106 108 104
5、条件Condition
使得线程等待,只有满足某条件时,才释放n个线程
import threading def run(n): con.acquire() con.wait() print("run the thread: %s" %n) con.release() if __name__ == '__main__': con = threading.Condition() for i in range(10): t = threading.Thread(target=run, args=(i,)) t.start() while True: n = input('>>>') if inp == 'q': break con.acquire() con.notify(int(n)) con.release()
输出结果如下: >>>3 >>>run the thread: 1 run the thread: 2 run the thread: 0 3 >>>run the thread: 3 run the thread: 4 run the thread: 5 3 >>>run the thread: 7 run the thread: 8 run the thread: 6 3 >>>run the thread: 9
6、Timer 定时器
定时器,指定n秒后执行某操作
from threading import Timer def hello(): print("hello, world") t = Timer(1, hello) t.start() 1s之后启动