• python 线程


    OK  ,看完进程,接下来看线程,线程是干什么呢的,跟进程有什么区别?

    进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:

      •   进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

      •   进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

        如果这两个缺点理解比较困难的话,举个现实的例子也许你就清楚了:如果把我们上课的过程看成一个进程的话,那么我们要做的是耳朵听老师讲课,手上还要记笔记,脑子还要思考问题,这样才能高效的完成听课的任务。而如果只提供进程这个机制的话,上面这三件事将不能同时执行,同一时间只能做一件事,听的时候就不能记笔记,也不能用脑子思考,这是其一;如果老师在黑板上写演算过程,我们开始记笔记,而老师突然有一步推不下去了,阻塞住了,他在那边思考着,而我们呢,也不能干其他事,即使你想趁此时思考一下刚才没听懂的一个问题都不行,这是其二。

        现在你应该明白了进程的缺陷了,而解决的办法很简单,我们完全可以让听、写、思三个独立的过程,并行起来,这样很明显可以提高听课的效率。而实际的操作系统中,也同样引入了这种类似的机制——线程。

    看见下边目录了吧,不少吧,没事,进程线程从程序方法上都是如出一辙的,只是字母不一样而已

    只要进程学明白了,线程就很好理解

    1. 线程的创建的两种方式
    2. 查看线程的进程id
    3. 验证线程是数据共享的
    4. 多进程和多线程的效率对比
    5. 锁  同步  互斥锁
    6. 死锁现象
    7. 递归锁
    8. 多线程的程序不结束多进程的程序不结束的区别
    9. 守护线程
    10. 子进程不能input

    一. 线程的创建的两种方式

    记住线程的单词,跟导入的单词字母,跟进程不一样

    上代码

    from threading import Thread         #线程的导入
    # from multiprocessing import Process     #进程的导入,看清除了哈

    第一种方式
    def   f1(n):
      print('%s号线程任务'%n)
    def   f2(n):
      print('%s号线程任务'%n)
    if __name__ == '__main__':
      t1 = Thread(target=f1,args=(1,))
      t2 = Thread(target=f2,args=(2,))
      t1.start()
      t2.start()
      print('主线程')


    第二种创建方式
    class  MyThread(Thread):
      def __init__(self,name):
      super(MyThread, self).__init__()
      super().__init__()
      self.name = name
      def   run(self):
        print('hello girl :' + self.name)
    if __name__ == '__main__':
      t = MyThread('alex')
      t.start()
      print('主线程结束')

    看吧基本上都一样,没太大区别,当然本质上是有区别的

    二. 查看线程的进程id

    import os
    from threading import Thread
    from multiprocessing import Process
    def   f1(n):
      print('1号',os.getpid())                 #这个功能没啥意思,把单词记住
      print('%s号线程任务'%n)
    def   f2(n):
      print('2号',os.getpid())
      print('%s号线程任务'%n)
    if __name__ == '__main__':
      t1 = Thread(target=f1,args=(1,))
      t2 = Thread(target=f2,args=(2,))
      t1.start()
      t2.start()
      print('主线程',os.getpid())
      print('主线程')

    三.验证线程是数据共享的

    import  os
    import  time
    from  threading  import  Thread
    # from multiprocessing import Process

    #通过对全局变量的修改来验证线程之间是数据共享的,共享同一进程中的数据
    num = 100
    def   f1(n):
      time.sleep(3)
      global num
      num = 3
    print('子线程的num',num)

    if __name__ == '__main__':
      t = Thread(target=f1,args=(1,))
      t.start()
    #等待子线程运行结束才继续往下执行
      t.join()

      print('主线程中的num:',num)

    看吧,最后结果跟进程的不一样,(主线程,跟子线程是数据共享的)。

    四.多进程和多线程的效率对比

    import   time
    from   threading   import   Thread
    from   multiprocessing   import   Process

    def   f1():
    # time.sleep(1) #io密集型
    # 计算型:
      n = 10
      for i in range(10000000):
        n = n + i

    if __name__ == '__main__':
    #查看一下20个线程执行20个任务的执行时间
      t_s_time = time.time()
      t_list = []
      for i in range(5):
        t = Thread(target=f1,)
        t.start()
        t_list.append(t)
    [tt.join() for tt in t_list]
    t_e_time = time.time()
    t_dif_time = t_e_time - t_s_time
    #查看一下20个进程执行同样的任务的执行时间
    p_s_time = time.time()
    p_list = []
    for i in range(5):
      p = Process(target=f1,)
      p.start()
      p_list.append(p)
    [pp.join() for pp in p_list]
    p_e_time = time.time()
    p_dif_time = p_e_time - p_s_time
    print('多线程的执行时间:',t_dif_time)
    print('多jincheng的执行时间:',p_dif_time)

    结果就是线程的运行速度是要比进程快的,这就是为什么要用线程的原因

    五. 锁  同步  互斥锁

    from   multiprocessing   import   Queue

    import  queue
    import  time
    from   threading   import  Lock,  Thread
    num = 100
    def  f1(loc):
      # num -= 1
      loc.acquire()
      global num
      # num -= 1
      tmp = num
      tmp -= 1
      time.sleep(0.001)
      num = tmp
      loc.release()
    if __name__ == '__main__':
      t_loc = Lock()
      t_list = []
      for i in range(10):
        t = Thread(target=f1,args=(t_loc,))
        t.start()
        t_list.append(t)
    [tt.join() for tt in t_list]
    print('主线的num',num)

    锁,把线程锁住,别乱套

    六.死锁现象

    进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额,进程的死锁和线程的是一样的,而且一般情况下进程之间是数据不共享的,不需要加锁,由于线程是对全局的数据共享的,所以对于全局的数据进行操作的时候,要加锁。

        所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁

    import   time
    from   threading   import   Thread, Lock, RLock

    def   f1(locA,locB):
      locA.acquire()                
      print('f1>>1号抢到了A锁')
      time.sleep(1)
      locB.acquire()
      print('f1>>1号抢到了B锁')
      locB.release()

      locA.release()
    def f2(locA,locB):
      locB.acquire()

      print('f2>>2号抢到了B锁')

      locA.acquire()
      time.sleep(1)
      print('f2>>2号抢到了A锁')
      locA.release()

      locB.release()
    if __name__ == '__main__':
      locA = Lock()
      locB = Lock()
      t1 = Thread(target=f1,args=(locA,locB))
      t2 = Thread(target=f2,args=(locA,locB))
      t1.start()
      t2.start()

    最后结果就是

    f1>>1号抢到了A锁
    f2>>2号抢到了B锁

    七.递归锁

    解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。

        这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:

    impor   time
    from   threading   import   Thread,  Lock,  RLock
    def   f1(locA, locB):
      # print('xxxx')
      # time.sleep(0.1)
      locA.acquire()
      print('f1>>1号抢到了A锁')

      time.sleep(1)
      locB.acquire()
      print('f1>>1号抢到了B锁')
      locB.release()

      locA.release()
    def  f2(locA, locB):
      print('22222')
      time.sleep(0.1)
      locB.acquire()
      print('f2>>2号抢到了B锁')
      locA.acquire()
      time.sleep(1)
      print('f2>>2号抢到了A锁')
      locA.release()
      locB.release()
    if __name__ == '__main__':
      #locA =locB = Lock()
      #locA = Lock()
      #locB = Lock()
      locA = locB = RLock()        #递归锁,维护一个计数器,acquire一次就加1,release就减1
      t1 = Thread(target=f1, args=(locA, locB))
      t2 = Thread(target=f2, args=(locA, locB))
      t1.start()
      t2.start()

    最后结果就是

    f1>>1号抢到了A锁
    22222              #主线程的快一点
    f1>>1号抢到了B锁
    f2>>2号抢到了B锁
    f2>>2号抢到了A锁

    八.多线程的程序不结束多进程的程序不结束的区别

    import   time
    from   threading   import   Thread
    from   multiprocessing   import   Process

    #守护进程:主进程代码执行运行结束,守护进程随之结束          #这两行字是重点,好好理解一下,敲一下

    #守护线程:守护线程会等待所有非守护线程运行结束才结束    #代码,看一下结果,对比一下

    def   f1():
      time.sleep(2)
      print('1号线程')

    def   f2():
      time.sleep(3)
      print('2号线程')
    if __name__ == '__main__':
      # t1 = Thread(target=f1,)
      # t2 = Thread(target=f2,)
      # t1.daemon = True
      # t2.daemon = True
      # t1.start()
      # t2.start()
      # print('主线程结束')
      t1 = Process(target=f1, )
      t2 = Process(target=f2, )
      # t1.daemon = True
      # # t2.daemon = True
      t1.start()
      t2.start()

      print('主进程结束')

    上面两行字是重点

    九.守护线程

    import   time
    from   threading   import   Thread
    from   multiprocessing   import   Process

    def   f1():
      time.sleep(2)
      print('1号线程')
    def   f2():
      time.sleep(3)
      print('2号线程')

    if __name__ == '__main__':
      t1 = Thread(target=f1,)
      t2 = Thread(target=f2,)
      # t1.daemon = True              #方法,这就是守护进程
      t2.daemon = True
      t1.start()
      t2.start()
      print('主线程结束')
      # t1 = Process(target=f1, )
      # t2 = Process(target=f2, )
      # t1.daemon = True
      # # t2.daemon = True
      # t1.start()
      # t2.start()
      #
      # print('主进程结束')

    十.子进程不能input

    from   threading   import   Thread
    from   multiprocessing   import   Process

    def   f1():
      name = input('请输入名字') #EOFError: EOF when reading a line
      #
      print(name)

    # if __name__ == '__main__':
      # input('主进程输入')
      # p = Process(target=f1,)
      # p.start()
      #
      # print('主进程结束')

    # if __name__ == '__main__':
      # input('主进程输入')
      p = Thread(target=f1,)
      p.start()

      print('主进程结束')

    子进程里面不能input,会报错

    子线程里面可以。

    线程知识点方法有点多,但还是那句话,跟进程差不太多,好好练一下,敲一下代码,单词记住,加油吧。

  • 相关阅读:
    C++之Static与Const
    LInux主机与虚拟机网络链接
    C#数据类型与数据类型转化
    C#网编Console(二)
    C#网编Winform(三)
    C#网编基础类与API(一)
    C实现CPU大小端判断
    QT程序图标设置
    四、初识Socket套接字API
    C++之继承(二)
  • 原文地址:https://www.cnblogs.com/python-lyy/p/10268383.html
Copyright © 2020-2023  润新知