python 多线程

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时,可以在遇到耗时操作时自动切换协程

运行结果如下:

 

原文地址:https://www.cnblogs.com/hgzero/p/8976827.html