线程也是实现多任务的一种方式,一个进程中,也经常需要同时做多件事,就需要同时运行多个‘子任务’,这些子任务就是线程。一个进程可以拥有多个并行的线程,其中每一个线程,共享当前进程的资源。
线程可以看出是轻量级的进程。多个线程共享内存,线程切换的开销小
进程和线程在使用上各有优缺点: 线程执行开销小, 但不利于资源的管理和保护, 而进程正相反。
在Python 程序中,可以通过“_thread”和 threading(推荐使用)这两个模块来处理线程。在 Python3 中, thread 模块已经废弃。可以使用 threading 模块代替。所以,在 Python3中不能再使用 thread 模块,但是为了兼容 Python3 以前的程序,在 Python3 中将 thread 模块重命名为“_thread”
使用_thread 模块
创建线程
当使用 thread 模块来处理线程时,可以调用里面的函数 start_new_thread()来生成一个新的线程
import _thread import time def fun1(): print('开始运行fun1') time.sleep(4) print('运行fun1结束') def fun2(): print('开始运行fun2') time.sleep(2) print('运行fun2结束') if __name__=='__main__': print('主进程开始运行') #启动一个线程运行函数 fun1 _thread.start_new_thread(fun1,()) #启动一个线程运行函数 fun2 _thread.start_new_thread(fun2,()) time.sleep(6) print('主进程结束运行')
fun1和fun2谁先开始不一定
为线程传递参数
import _thread import time def fun1(thread_name,delay): print('线程{0}开始运行 fun1'.format(thread_name)) time.sleep(delay) print('线程{0}运行 fun1 结束'.format(thread_name)) def fun2(thread_name,delay): print('线程{0}开始运行 fun2'.format(thread_name)) time.sleep(delay) print('线程{0}运行 fun2 结束'.format(thread_name)) if __name__=='__main__': print('开始运行') #启动一个线程运行函数 fun1 _thread.start_new_thread(fun1,('thread-1',4)) #启动一个线程运行函数 fun2 _thread.start_new_thread(fun2,('thread-2',2)) time.sleep(6)
_thread 模块
Python3 通过两个标准库 _thread 和 threading 提供对线程的支持。 _thread 提供了低级别的、原始的线程以及一个简单的锁,它相比于 threading 模块的功能还是比较有限的
在 Python3 程序中,对多线程支持最好的是 threading 模块,使用这个模块,可以灵活地创建多线程程序,并且可以在多线程之间进行同步和通信。
在 Python3 程序中,可以通过如下两种方式来创建线程:
- 通过 threading.Thread 直接在线程中运行函数
- 通过继承类 threading.Thread 来创建线程
通过 threading.Thread 直接在线程中运行函数
import threading import time def fun1(thread_name,delay): print('线程{0}开始运行fun1'.format(thread_name)) time.sleep(delay) print('线程{0}运行fun1结束'.format(thread_name)) def fun2(thread_name,delay): print('线程{0}开始运行fun2'.format(thread_name)) time.sleep(delay) print('线程{0}运行fun2结束'.format(thread_name)) if __name__=='__main__': print('开始运行') #创建线程 t1=threading.Thread(target=fun1,args=('thread-1',2)) t2=threading.Thread(target=fun2,args=('thread-2',4)) t1.start() t2.start()
继承 threading.Thread 类创建线程
import threading import time def fun1(delay): print('线程{0}开始运行 fun1'.format(threading.current_thread().getName())) time.sleep(delay) print('线程{0}运行 fun1 结束'.format(threading.current_thread().getName())) def fun2(delay): print('线程{0}开始运行 fun2'.format(threading.current_thread().getName())) time.sleep(2) print('线程{0}运行 fun2 结束'.format(threading.current_thread().getName())) #创建线程类继承 threading.Thread class MyThread(threading.Thread): #重写父类的构造方法,其中 func 是线程函数, args 是传入线程的参数,name 是线程名 def __init__(self,func,name,args): super().__init__(target=func,name=name,args=args) #重写父类的 run()方法 def run(self): self._target(*self._args) if __name__=='__main__': print('开始运行') #创建线程 t1=MyThread(fun1,'thread-1',(2,)) t2=MyThread(fun2,'thread-2',(4,)) t1.start() t2.start()
线程共享全局变量
在一个进程内所有线程共享全局变量,多线程之间的数据共享比多进程要好。但是可能造成多个进程同时修改一个变量(即线程非安全),可能造成混乱
import time from threading import * #定义全局变量 num num=10 def test1(): global num for i in range(3): num+=1 print('test1 输出 num:',num) def test2(): global num print('test2 输出 num:',num) if __name__=='__main__': t1=Thread(target=test1) t2=Thread(target=test2) t1.start() t1.join() t2.start() t2.join()
线程共享全局变量存在问题
t1和t2谁先执行都是随机的
import time from threading import * #定义全局变量 num num=0 def test1(): global num for i in range(100000): num+=1 print('执行test1函数num的值:',num) def test2(): global num for i in range(100000): num+=1 print('执行test2函数num的值:',num) if __name__=='__main__': t1=Thread(target=test1) t2=Thread(target=test2) t1.start() t2.start() t1.join() t2.join()
按理说结果应该是100000和200000
互斥锁
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。 最简单的同步机制就是引入互斥锁。
锁有两种状态——锁定和未锁定。 某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”状态,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
使用 Thread 对象的 Lock 可以实现简单的线程同步,有上锁 acquire 方法和 释放release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和release 方法之间
就和操作系统学的加锁机制一样
import time from threading import Thread,Lock #定义全局变量 num num=0 #创建一把互斥锁 mutex=Lock() def test1(): global num ''' 在两个线程中都调用上锁的方法,则这两个线程就会抢着上锁, 如果有 1 方成功上锁,那么导致另外一方会堵塞(一直等待)直到这个锁被解开 ''' mutex.acquire()#上锁 for i in range(100000): num+=1 mutex.release() print('test1 输出 num:',num) def test2(): global num mutex.acquire() # 上锁 for i in range(100000): num+=1 mutex.release() print('test2 输出 num:',num) if __name__=='__main__': t1=Thread(target=test1) t2=Thread(target=test2) t1.start() t2.start() t1.join() t2.join()
互斥锁改进
只对相加操作加锁,而不是对整个for循环加锁了
import time from threading import Thread,Lock #定义全局变量 num num=0 #创建一把互斥锁 mutex=Lock() def test1(): global num ''' 在两个线程中都调用上锁的方法,则这两个线程就会抢着上锁, 如果有 1 方成功上锁,那么导致另外一方会堵塞(一直等待)直到这个锁被解开 ''' for i in range(100000): mutex.acquire() # 上锁 num+=1 mutex.release() print('test1 输出 num:',num) def test2(): global num for i in range(100000): mutex.acquire() # 上锁 num+=1 mutex.release() print('test2 输出 num:',num) if __name__=='__main__': t1=Thread(target=test1) t2=Thread(target=test2) t1.start() t2.start() t1.join() t2.join()
死锁
在线程共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁
import time from threading import Thread,Lock import threading mutexA=threading.Lock() mutexB=threading.Lock() class MyThread1(Thread): def run(self): if mutexA.acquire(): print(self.name,'执行') time.sleep(1) if mutexB.acquire(): print(self.name,'执行') mutexB.release() mutexA.release() class MyThread2(Thread): def run(self): if mutexB.acquire(): print(self.name,'执行') time.sleep(1) if mutexA.acquire(): print(self.name,'执行') mutexA.release() mutexB.release() if __name__ == '__main__': t1=MyThread1() t2=MyThread2() t1.start() t2.start()
一直卡在那了