• 18、Python之多线程


    一、进程与线程概念

         首先说一下进程和程序的区别:程序是静态的,是存在磁盘上的,而进程是在执行中的程序,是在内存中的。起初没有线程的概率,只有进程,一个进程它有独立的资源,这就好比我们把一个班级看做一个进程,黑板,桌椅都是这个班级进程的资源,别的进程(班级)无法享用。对于外界(CPU等)而言班级是一个进程,而实际上在班级中还有很多学生,他们是一个个独立的个体,共享着班级的资源,但由于外界并不知道,一旦班级这个进程被阻塞了,班级内部的学生都无法进行活动了,这显然不适合计算机的高效,因而产生了线程(学生)被外界所认可(cpu)。由此我们可以得出进程和线程的结论为:

        1、进程是作为最小资源分配单位

        2、线程是独立运行的最小单位(被cpu调用)

        3、一个进程下有多个线程,且他们共享进程的资源

        4、一个进程至少有一个线程

       GIL(Global Interpreter Lock):全局解释器锁

       可能有些人在学python之前就听说过,python有一个缺点就是无法执行多线程,这正是由于GIL的原因。但是,需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL。

    二、Python中线程调用的2种方式

        方式一、直接调用

    1 import threading
    2 def run(n):
    3     print(n)
    4 
    5 t1 = threading.Thread(target=run,args=("test1",))
    6 t2 = threading.Thread(target=run,args=("test2",))
    7 t1.start()
    8 t2.start()
    View Code

    上面程序中一共有3个线程,t1,t2以及运行t1,t2的主线程,我们可以使用语句threading.activeCount()打印出当前活跃线程的数量。

      方式二、继承式调用

     1 import threading
     2 
     3 class MyThread(threading.Thread):
     4     def __init__(self,name):
     5         super(MyThread,self).__init__()
     6         self.__name = name
     7     def run(self):
     8         print(self.__name)
     9 
    10 t1 = MyThread("test1")
    11 t2 = MyThread("test2")
    12 t1.start()
    13 t2.start()
    View Code

    三、多线程中的注意点

       1、多线程中,只有一个主线程,主线程可创建多个子线程,子线程一旦被创建后,就与主线程没有半毛钱的关系。因而我们将上面的代码稍微改造一下。

     1 import threading,time
     2 
     3 class MyThread(threading.Thread):
     4     def __init__(self,name):
     5         super(MyThread,self).__init__()
     6         self.__name = name
     7     def run(self):
     8         time.sleep(1)
     9         print("线程%s运行完毕" % self.__name)
    10 
    11 t1 = MyThread("test1")
    12 t2 = MyThread("test2")
    13 t1.start()
    14 t2.start()
    15 print("主线程运行完毕")
    View Code

    运行结果为:

    主线程运行完毕
    线程test1运行完毕
    线程test2运行完毕

    由此可见,一旦主线程创建了子线程之后,将继续运行自己的代码,而子线程独立运行,但是有时候我们期望等待某个线程的执行结束,再运行主线程,这时候需要使用方法join()

    上述代码要想主线程在2个子线程执行完之后再执行,代码改造如下:

     1 import threading,time
     2 
     3 class MyThread(threading.Thread):
     4     def __init__(self,name):
     5         super(MyThread,self).__init__()
     6         self.__name = name
     7     def run(self):
     8         time.sleep(1)
     9         print("线程%s运行完毕" % self.__name)
    10 
    11 t1 = MyThread("test1")
    12 t2 = MyThread("test2")
    13 t1.start()
    14 t2.start()
    15 t1.join()
    16 t2.join()
    17 print("主线程运行完毕")
    View Code

        2、当主线程创建一个守护线程的时,一旦主线程运行结束,守护线程立即结束。在创建线程时使用setDaemon(True)方法就可以将子线程设置为守护线程。

     1 import threading,time
     2 
     3 class MyThread(threading.Thread):
     4     def __init__(self,name):
     5         super(MyThread,self).__init__()
     6         self.__name = name
     7     def run(self):
     8         time.sleep(1)
     9         print("线程%s运行完毕" % self.__name)
    10 
    11 t1 = MyThread("test1")
    12 t2 = MyThread("test2")
    13 t1.setDaemon(True) #将线程t1设置为守护线程
    14 t2.setDaemon(True) #将线程t2设置为守护线程
    15 t1.start()
    16 t2.start()
    17 print("主线程运行完毕")
    View Code

    运行结果为:主线程运行完毕,子线程还未运行完毕,就结束了。

        3、线程锁:假设有这样一个程序:

     1 import threading,time,random
     2 
     3 class MyThread(threading.Thread):
     4     count = 0
     5     def __init__(self,name):
     6         super(MyThread,self).__init__()
     7         self.__name = name
     8     def run(self):
     9         time.sleep(random.randrange(1,10))
    10         MyThread.count += 1
    11         print("线程%s运行完毕" % self.__name)
    12     @classmethod
    13     def print_count(self):
    14         print(MyThread.count)
    15 
    16 obj_list = []
    17 for i in range(50):
    18     t = MyThread(i)
    19     t.start()
    20     obj_list.append(t)
    21 for obj in obj_list:
    22     obj.join()
    23 MyThread.print_count()#打印总共的线程数
    24 print("主线程运行完毕")
    View Code

    50个线程一旦被创建后,都会去修改count的值,如果不加锁的话,就会导致count最终的值是错误的(据说python三种做了处理,但是没有官方声明)。为了使count的值最终正确,我们需要给这段代码加一个锁。假设的三步骤:1、申请一把锁:lock = threading.Lock()  2、加锁 lock.acquire() 3、还锁  lock.release()。

        所以正确的代码应该是:

     1 import threading,time,random
     2 
     3 lock = threading.Lock() #申请锁
     4 class MyThread(threading.Thread):
     5     count = 0
     6     def __init__(self,name):
     7         super(MyThread,self).__init__()
     8         self.__name = name
     9     def run(self):
    10         time.sleep(random.randrange(1,10))
    11         lock.acquire() #加锁
    12         MyThread.count += 1
    13         lock.release() #释放锁
    14         print("线程%s运行完毕" % self.__name)
    15     @classmethod
    16     def print_count(self):
    17         print(MyThread.count)
    18 
    19 obj_list = []
    20 for i in range(50):
    21     t = MyThread(i)
    22     t.start()
    23     obj_list.append(t)
    24 for obj in obj_list:
    25     obj.join()
    26 MyThread.print_count()#打印总共的线程数
    27 print("主线程运行完毕")
    View Code

    四、信号量

        有时候我们系统共享的资源可能有多个,这时候需要用到信号量,信号量的使用代码示例如下:

     1 import threading,time
     2 
     3 semaphore = threading.BoundedSemaphore(5)
     4 def run(n):
     5     semaphore.acquire()
     6     time.sleep(2)
     7     print(n+1)
     8     semaphore.release()
     9 
    10 for i in range(23):
    11     t = threading.Thread(target=run,args=(i,))
    12     t.start()
    13 while threading.active_count()!=1:
    14     pass
    15 else:
    16     print("over")
    View Code

    五、递归锁

        我们申请的每一把锁都应该记住其对应的对象地址,否则当有多把锁时,就会容易导致程序死锁。示例代码如下:

     1 import threading
     2 n = 0
     3 lock = {}
     4 lock["1"] = threading.Lock()
     5 lock["2"] = threading.Lock()
     6 lock["3"] = threading.Lock()
     7 #应该记清楚每一把锁
     8 def run1():
     9     global n
    10     lock["1"].acquire()
    11     print("run1")
    12     n = n + 1
    13     lock["1"].release()
    14 
    15 def run2():
    16     global n
    17     lock["2"].acquire()
    18     print("run2")
    19     n = n + 1
    20     lock["2"].release()
    21 
    22 def run3():
    23     global n
    24     lock["3"].acquire()
    25     run1()
    26     run2()
    27     lock["3"].release()
    28 
    29 t = threading.Thread(target=run3)
    30 t.start()
    31 while threading.active_count()!=1:
    32     print(threading.active_count())
    33 else:
    34     print("over")
    View Code

    六、事件

        事件其本质就是设置一个标志位,当线程在运行的时候,判断这个标志位,如果标志位未被设置就阻塞。下面是红绿灯的一个代码示例:

     1 import threading,time,random
     2 
     3 event = threading.Event()#获取一个event对象
     4 
     5 def lighter():
     6     count = 0
     7     event.set() #设置标志位
     8     while True:
     9         if count >=0 and count<10:
    10             event.clear() #清除标志位
    11             print("33[41;1mred light is on...33[0m")
    12         elif count>=10 and count<20:
    13             event.set()
    14             print("33[42;1mgreen light is on...33[0m")
    15         else:
    16             count = 0
    17         time.sleep(1)
    18         count = count + 1
    19 
    20 t = threading.Thread(target=lighter)
    21 t.start()
    22 
    23 def car(n):
    24     while True:
    25         if event.is_set():#判断标志位是否被设置
    26             print("the car is passing the accross")
    27             time.sleep(random.randrange(1,10))
    28             print("the %s car has accrosee the across..." % n)
    29             break
    30         else:
    31             print("the %s car is wait..." % n)
    32             event.wait()
    33 
    34 for i in range(20):
    35     t = threading.Thread(target=car,args=(i,))
    36     t.start()
    View Code
  • 相关阅读:
    基于Appium的自动化case开发及case分层结构设计
    功能自动化接入持续集成方案
    Windows上部署Appium自动化测试框架
    Mac上部署Appium测试框架
    Appium原理简述
    开篇
    数据结构和算法动态可视化
    Request实现简易注册登录
    过滤器解决中文乱码
    简易登录拦截(没有登录前直接访问主页则跳转到登录页)
  • 原文地址:https://www.cnblogs.com/win0211/p/8549643.html
Copyright © 2020-2023  润新知