1.调用threading模块来创建线程
1 from threading import Thread 2 import threading 3 import os 4 import time 5 6 def func(num): 7 for i in range(5): 8 time.sleep(0.1) 9 print("当前线程号为<--%d--> 它是子线程中的第<--%d-->个"%(num, i)) 10 11 # 创建出5个进程 12 for i in range(4): 13 T = Thread(target=func, args=(i,)) 14 T.start() 15 16 # 查看当前的线程数量 , 包含主进程 17 num_threading = len(threading.enumerate()) 18 print(num_threading)
运行结果如下:
线程的调用是随机的,它和进程一样,取决于系统的调度算法, 线程运行的实质是每一个线程在cpu的一个核心上轮流占用
2.调用threading.Thread的子类来创建多线程
1 import threading 2 import time 3 4 class play(threading.Thread): 5 def __init__(self, num): 6 # 这里继承父类的__init__方法是因为防止父类的__init__方法被重写 7 threading.Thread.__init__(self) 8 self.num = num 9 10 def run(self): # 这里必须定义一个run方法, 因为调用线程时run方法决定了类的执行方式 11 time.sleep(0.1) 12 for i in range(5): 13 print("当前线程号为<--%d--> 它是子线程中的第<--%d-->个"%(self.num, i)) 14 15 for i in range(5): 16 t = play(i) # 这里可以直接实例化一个线程的类,直接开启线程 17 t.start()
通过这种方法来创建线程时,一定要在继承threading.Thread的子类中定义一个run方法,当调用这个类时,会直接自动调用run方法
因为线程实际上是共用一个cpu的核心的,所以线程之间的数据是可用共享的, 一个进程内的所有线程共享全局变量
进程和线程的区别:
进程,是系统进行资源分配和调度的一个独立单位
线程,是cpu调度和分派的基本单位
线程不安全现象:
若不能有效的控制多个进程对同一资源的访问,会对资源数据产生破坏,使得线程的结果不可预期
3.互斥锁(解决线程不安全)
为了解决线程不安全,引入了互斥锁,当某个线程要修改共享数据时,先将其锁定,资源的访问状态为: 锁定 ,此时其他线程不能更改;一直到该线程释放了资源,这时资源的访问状态更改为:非锁定,其他线程才能继续锁定该资源,保证了每次只有一个线程在操作共享资源。
1 from threading import Thread, Lock 2 3 num = 0 4 5 def func1(): 6 global num 7 for i in range(100): 8 L.acquire(True) # acquire中的参数True表示堵塞,若资源被其他线程上锁则一直等待 False则表示非阻塞 9 num += 1 10 L.release() # 将资源解锁 11 print(num) 12 13 def func2(): 14 global num 15 for i in range(100): 16 L.acquire(True) 17 num +=1 18 L.release() 19 print(num) 20 21 L = Lock() 22 23 t1 = Thread(target=func1, ) 24 t2 = Thread(target=func2, ) 25 26 t1.start() 27 t2.start()
acquire() 表示上锁,release() 表示解锁
在多线程中,全局变量是在线程间共享的,而局部变量是属于各自线程的,不是共享的
4.ThreadLocal在单个线程中共享变量
1 import threading 2 import os 3 4 th_local = threading.local() # 创建一个全局变量 ,但是其中的每个属性都是单个线程的局部变量 5 6 def func2(): 7 print("hello %s"%(th_local.person)) 8 9 def func1(name): 10 print("--->>> %d"%(os.getpid())) 11 th_local.person = name # 将name传递给th_local的person属性,仅能在这个线程中共享 12 func2() 13 14 t1 = threading.Thread(target=func1, args=("laowang",)) 15 t2 = threading.Thread(target=func1, args=("hgzero",)) 16 17 t1.start() 18 t2.start() 19 20 t1.join() 21 t2.join()
首先创建出一个threading.local()的全局变量, 设置单个线程内的共享数据时只需将数据保存到threading.local()的属性中即可, 每个线程中的threading.local()都相当于全局变量的一个拷贝,在线程之间相互独立,而在单个线程内共享数据
在io密集型的操作中,为了充分的利用CPU的性能,在io操作过程中去执行其他东西,由此便引入协程
1.用yield来实现协程
1 import time
2
3 def A(): # 用yield创建出一个生成器
4 while True:
5 yield
6 print("---> hgzero")
7 time.sleep(0.1)
8
9 def B(n):
10 while True:
11 n.__next__() # 切换到A函数中
12 print("---> laowang")
13 time.sleep(0.1)
14
15 n = A()
16 B(n)
2.用greenlet来实现协程
使用greenlet可以在遇到io操作时,手动的切换到其他的任务中去, greenlet使用时需要事先安装
1 from greenlet import greenlet # greenlet 模块需要安装
2 import time
3
4 def func1():
5 while True:
6 print("---> hgzero")
7 g2.switch() # 切换到函数2中去
8 time.sleep(0.2)
9
10 def func2():
11 while True:
12 print("---> laowang")
13 g1.switch() # 切换到函数1中去
14 time.sleep(0.2)
15
16 g1 = greenlet(func1)
17 g2 = greenlet(func2)
18
19 g1.switch()
greenlet在使用时用switch()方法来进行任务的切换
3.使用gevent实现协程
为了避免greenlet在切换任务时需要手动的繁琐,使用gevent可以有效的避免这个问题, gevent可以在碰到io操作时,自动的切换协程
1 import gevent
2
3 def func(i):
4 for i in range(i):
5 print(gevent.getcurrent(),i)
6 gevent.sleep(0.2) # 模拟一个io操作 注意这里要使用gevent模块中的sleep()
7
8 s1 = gevent.spawn(func,5) # 这里的第二个参数为要传递递给func函数的值
9 s2 = gevent.spawn(func,5)
10 s3 = gevent.spawn(func,5)
11
12 s1.join()
13 s2.join()
14 s3.join()
使用gevent时,可以在遇到耗时操作时自动切换协程
运行结果如下: