多线程介绍
什么是线程
线程(Thread)也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。
个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
为什么需要多线程
- 进程之间不能共享内存,但线程之间共享内存非常容易。
- 使用多线程来实现多任务并发执行比使用多进程的效率高。
- 多个线程需要并发处理,并共享处理结果。
多线程实现方法
创建线程
1.普通创建线程方法
-
调用threading模块:
import threading,time def run(input): for i in range(10): print('sub task:%s'%input) time.sleep(1) if __name__ == '__main__': t = threading.Thread(target=run, args=("hello world",)) t.start() for i in range(10): print('main task') time.sleep(1)
2.Thread类继承方法
-
新建类,继承自threading.Thread类:
import threading import time class MyThread(threading.Thread): def __init__(self, input): super(MyThread, self).__init__() # 重构run函数必须要写 self.input = input def run(self): for i in range(10): print("task", self.input) time.sleep(1) if __name__ == "__main__": t1 = MyThread("hello world 1") t2 = MyThread("hello world 2") t1.start() t2.start()
守护线程
1.设置子线程为守护线程
-
使用setDaemon(True)把所有的子线程都变成了主线程的守护线程,因此当主进程结束后,子线程也会随之结束。
import threading import time def run(n): for i in range(10): print("task", self.input) time.sleep(1) if __name__ == '__main__': t = threading.Thread(target=run, args=("hello world",)) t.setDaemon(True) # 主线程kill,子线程自动kill t.start() print("end")
2.主线程等待子线程结束后执行下一步
-
为了让守护线程执行结束之后,主线程再结束,我们可以使用join方法,让主线程等待子线程执行。
import threading import time def run(n): for i in range(10): print('sub task') time.sleep(1) if __name__ == '__main__': t = threading.Thread(target=run, args=("t1",)) t.setDaemon(True) # 主线程kill,子线程自动kill t.start() t.join() # 主线程停止等待子线程执行完毕后,再继续执行! # 注释掉之后,就是子线程、主线程同时跑!
3.多线程共享全局变量
-
线程是进程的执行单元,进程是系统分配资源的最小单位,所以在同一个进程中的多线程是共享资源的。
import threading import time g_num = 100 def run(n): global g_num for i in range(10): print('sub task') g_num += 1 time.sleep(1) if __name__ == '__main__': t = threading.Thread(target=run, args=("t1",)) t.setDaemon(True) # 主线程kill,子线程自动kill t.start() # t.join() # 主线程停止等待子线程执行完毕后,再继续执行! # 注释掉之后,就是子线程、主线程同时跑! for i in range(10): print('main task:%d'%g_num) time.sleep(1)
4.互斥锁
-
当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁,即同一时刻允许一个线程执行操作。
-
如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期,我们也称此为“线程不安全”。
-
线程锁用于锁定资源,你可以定义多个锁, 像下面的代码, 当你需要独占某一资源时,任何一个锁都可以锁这个资源,就好比你用不同的锁都可以把相同的一个门锁住是一个道理。
from threading import Thread,Lock import os,time,threading reg = 0 def work1(): global reg lock.acquire() for i in range(10): reg = i print(reg) time.sleep(0.5) lock.release() def work2(): global reg lock.acquire() for i in range(10): reg = i * 10 print(reg) time.sleep(0.5) lock.release() if __name__ == '__main__': lock=Lock() t1 = threading.Thread(target=work1) t2 = threading.Thread(target=work2) t1.start() t2.start() t1.join() # hold on ,wait for this thread t2.join() # hold on ,wait for this thread
5.递归锁
-
RLcok类的用法和Lock类一模一样,但它支持嵌套,在多个锁没有释放的时候一般会使用RLcok类。
import threading import time def Func(lock): global gl_num lock.acquire() gl_num += 1 time.sleep(1) print(gl_num) lock.release() if __name__ == '__main__': gl_num = 0 lock = threading.RLock() for i in range(10): t = threading.Thread(target=Func, args=(lock,)) t.start()
6.信号量
-
互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
import threading import time def run(n, semaphore): semaphore.acquire() #加锁 time.sleep(1) print("run the thread:%s\n" % n) semaphore.release() #释放 if __name__ == '__main__': num = 0 semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行 for i in range(22): t = threading.Thread(target=run, args=("t-%s" % i, semaphore)) t.start() while threading.active_count() != 1: pass # print threading.active_count() else: print('-----all threads done-----')
7.事件(Event类)
-
python线程的事件用于主线程控制其他线程的执行,事件是一个简单的线程同步对象,其主要提供以下几个方法:
- clear 将flag设置为“False”
- set 将flag设置为“True”
- is_set 判断是否设置了flag
- wait 会一直监听flag,如果没有检测到flag就一直处于阻塞状态
-
事件处理的机制:全局定义了一个“Flag”,当flag值为“False”,那么event.wait()就会阻塞,当flag值为“True”,那么event.wait()便不再阻塞。
#利用Event类模拟红绿灯 import threading import time event = threading.Event() # 前5秒是绿灯,5~10秒是红灯。 def lighter(): count = 0 event.set() #初始值为绿灯 while True: if 5 < count <=10 : event.clear() # 红灯,清除标志位 print("\33[41;1mred light is on...\033[0m") elif count > 10: event.set() # 绿灯,设置标志位 count = 0 else: print("\33[42;1mgreen light is on...\033[0m") time.sleep(1) count += 1 def car(name): while True: if event.is_set(): #判断是否设置了标志位 print("[%s] running..."%name) time.sleep(1) else: print("[%s] sees red light,waiting..."%name) event.wait() print("[%s] green light is on,start going..."%name) light = threading.Thread(target=lighter,) light.start() car = threading.Thread(target=car,args=("MINI",)) car.start()
Python多线程内在逻
- 在非python环境中,单核情况下,同时只能有一个任务执行。多核时可以支持多个线程同时执行。但是在python中,无论有多少核,同时只能执行一个线程。
- GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。
- Python多线程的工作过程:(python在使用多线程的时候,调用的是c语言的原生线程)
- 拿到公共数据
- 申请gil
- python解释器调用os原生线程
- os操作cpu执行运算
- 当该线程执行时间到后,无论运算是否已经执行完,gil都被要求释放
- 进而由其他进程重复上面的过程
- 等其他进程执行完后,又会切换到之前的线程(从他记录的上下文继续执行),整个过程是每个线程执行自己的运算,当执行时间到就进行切换(context switch)。
参考教程
标签:__,Pyhton,threading,线程,time,print,import,多线程 From: https://www.cnblogs.com/weicun581/p/16849993.html