zoukankan      html  css  js  c++  java
  • 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时,可以在遇到耗时操作时自动切换协程

    运行结果如下:

     

  • 相关阅读:
    BZOJ 1009 GT考试
    BZOJ 2085 [POI2010] Hamsters
    BZOJ 3160 万径人踪灭
    左偏树 / 非旋转treap学习笔记
    BZOJ 3217 ALOEXT
    BZOJ 3065 带插入区间第K小值
    BZOJ2716 天使玩偶
    XSY1659 [HNOI2012]永无乡
    BZOJ1367【Baltic2004】sequence
    蔡勒公式 计算星期
  • 原文地址:https://www.cnblogs.com/hgzero/p/8976827.html
Copyright © 2011-2022 走看看