• python-day34--并发编程之多线程


    理论部分

    一、什么是线程:

    1.线程:一条流水线的工作过程 

    2.一个进程里至少有一个线程,这个线程叫主线程

      进程里真正干活的就是线程

    3.进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。

    4.多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。

      共享就存在竞争,----加锁-----队列

     1 from multiprocessing import Process
     2 from threading import Thread
     3 import os
     4 import time
     5 n=100
     6 def work():
     7     global n
     8     n-=100
     9 
    10 if __name__ == '__main__':
    11     p=Thread(target=work,)
    12     p.start()
    13     p.join()
    14     print('',n)
    15 
    16 #结果是0
    View Code

    二、线程的创建开销小,因而创建线程的速度快

     1 from multiprocessing import Process
     2 from threading import Thread
     3 import os
     4 import time
     5 def work():
     6     print('<%s> is running' %os.getpid())
     7     time.sleep(2)
     8     print('<%s> is done' %os.getpid())
     9 
    10 if __name__ == '__main__':
    11     t=Thread(target=work,)
    12     t.start()
    13     print('',os.getpid())
    14 
    15 
    16 结果:
    17 <20348> is running
    18 主 20348
    19 <20348> is done
    View Code

    1.进程之间内存空间是互相隔离的

    三、线程与进程的区别

      1.线程共享创建它的进程的地址空间;进程有自己的地址空间。

      2.线程可以直接访问其进程的数据段;进程有自己的父进程数据段的副本。(Linux系统,Windows系统也可以这么说,但是要注意Windows没有子进程的概念)

      3.线程可以与进程的其他线程直接通信;进程必须使用进程间通信来与同级进程通信。

      4.新线程很容易创建;新进程需要父进程的重复。

    总之记住两点就ok: 1、多线程共享它们进程的资源,2、线程的创建开销小

     1 from multiprocessing import Process
     2 from threading import Thread
     3 import os
     4 import time
     5 n=100
     6 def work():
     7     global n
     8     n-=100
     9 
    10 if __name__ == '__main__':
    11     # p=Process(target=work,)
    12     p=Thread(target=work,)
    13     p.start()
    14     p.join()
    15     print('',n)
    View Code

      5.扩展:分布式与集中式(所有的东西放在一起)

    四、为什么实用多线程:

      1、多线程共享它们进程的资源,2、线程的创建开销小

    五、多线程的应用举例

      开启一个字处理软件进程,该进程肯定需要办不止一件事情,比如监听键盘输入,处理文字,定时自动将文字保存到硬盘,这三个任务操作的都是同一块数据,因而不能用多进程。只能在一个进程里并发地开启三个线程,如果是单线程,那就只能是,键盘输入时,不能处理文字和自动保存,自动保存时又不能输入和处理文字。 

     

    代码部分

    六、开启进程的两种方式:

     1 #方式一
     2 from threading import Thread
     3 import time
     4 def sayhi(name):
     5     time.sleep(2)
     6     print('%s say hello' %name)
     7 
     8 if __name__ == '__main__':
     9     t=Thread(target=sayhi,args=('egon',))
    10     t.start()
    11     print('主线程')
    方式一
     1 #方式二
     2 from threading import Thread
     3 import time
     4 class Sayhi(Thread):
     5     def __init__(self,name):
     6         super().__init__()
     7         self.name=name
     8     def run(self):
     9         time.sleep(2)
    10         print('%s say hello' % self.name)
    11 
    12 
    13 if __name__ == '__main__':
    14     t = Sayhi('egon')
    15     t.start()
    16     print('主线程')
    方式二

    七、多线程共享同一个进程内的地址空间

      多线程共享——>竞争——>锁

    八、练习

    from socket import *
    from threading import Thread
    s=socket(AF_INET,SOCK_STREAM)
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(('192.168.20.95',8082))
    s.listen(5)
    def talk(conn):
        while True:
            try:
                cmd=conn.recv(1024)
                if not cmd:break
                conn.send(cmd.upper())
            except Exception:
                break
        conn.close()
    
    if __name__ == '__main__':
        while True:
            conn,addr=s.accept()
            p=Thread(target=talk,args=(conn,))
            p.start()
        s.close()
    多线程并发的socket服务端
     1 from socket import *
     2 c=socket(AF_INET,SOCK_STREAM)
     3 c.connect(('192.168.20.95',8082))
     4 
     5 while True:
     6     msg=input('>>: ').strip()
     7     if not msg:continue
     8     c.send(msg.encode('utf-8'))
     9     data=c.recv(1024)
    10     print(data.decode('utf-8'))
    11 c.close()
    客户端

    练习二:三个任务,一个接收用户输入,一个将用户输入的内容格式化成大写,一个将格式化后的结果存入文件

     1 from threading import Thread
     2 input_l=[]
     3 format_l=[]
     4 def talk():
     5     while True:
     6         msg=input('>>: ')
     7         if not msg:continue
     8         input_l.append(msg)
     9 def format():
    10     while True:
    11         if input_l:
    12             res=input_l.pop()
    13             format_l.append(res.upper())
    14 def save():
    15     with open('db.txt','a') as f:
    16         while True:
    17             if format_l:
    18                 f.write('%s
    ' %(format_l.pop()))
    19                 f.flush()
    20 
    21 
    22 if __name__ == '__main__':
    23     t1=Thread(target=talk)
    24     t2=Thread(target=format)
    25     t3=Thread(target=save)
    26 
    27     t1.start()
    28     t2.start()
    29     t3.start()
    View Code

    九、线程相关的其他属性及方法(了解)

    1 Thread实例对象的方法
    2   # isAlive(): 返回线程是否活动的。
    3   # getName(): 返回线程名。
    4   # setName(): 设置线程名。
    5 
    6 threading模块提供的一些方法:
    7   # threading.currentThread(): 返回当前的线程变量。
    8   # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
    9   # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
    View Code
     1 from threading import Thread,currentThread,activeCount
     2 import os,time,threading
     3 def talk():
     4     print('%s is running' %currentThread().getName())
     5 
     6 if __name__ == '__main__':
     7     # t=Thread(target=talk,name='egon')
     8     t=Thread(target=talk)
     9     t.start()
    10     print(t.name)
    11     print(t.getName())
    12     print(t.is_alive())
    13     print(currentThread().getName())
    14     print(threading.enumerate())
    15     print('',activeCount())
    16 
    17 
    18 
    19 #结果
    20 Thread-1 is running
    21 Thread-1
    22 Thread-1
    23 False
    24 MainThread
    25 [<_MainThread(MainThread, started 33752)>]
    26 主 1
    View Code

    十、守护进程

      进程: 主进程代码完——>守护死

      线程: 主线程的非守护线程完——>守护死

    十一、全局解释器锁:(GIL——CPython解释器的)

      1.保护系统级别的(CPython解释器)代码的安全,但是不能保护用户级别的数据(软件产生的代码)安全

      2.抢锁: 所有线程抢的是GIL锁,或者说所有线程抢的是执行权限

      3.释放锁: ①I/O阻塞  ②运行时间过长

      4.在一个进程中多个线程通过GIL就会变成串行的效果

      5.有了GIL的存在,同一时刻同一进程中只有一个线程被执行

      6.应用:

        多线程用于IO密集型,如socket,爬虫,web
        多进程用于计算密集型,如金融分析

    十二、同步锁:

      GIL VS Lock

      过程分析:所有线程抢的是GIL锁,或者说所有线程抢的是执行权限

      线程1抢到GIL锁,拿到执行权限,开始执行,然后加了一把Lock,还没有执行完毕,即线程1还未释放Lock,有可能线程2抢到GIL锁,开始执行,执行过程中发现Lock还没有被线程1释放,于是线程2进入阻塞,被夺走执行权限,有可能线程1拿到GIL,然后正常执行到释放Lock。。。这就导致了串行运行的效果

      既然是串行,那我们执行

      t1.start()

      t1.join

      t2.start()

      t2.join()

      这也是串行执行啊,为何还要加Lock呢,需知join是等待t1所有的代码执行完,相当于锁住了t1的所有代码,而Lock只是锁住一部分操作共享数据的代码。

    1 分析:
    2   #1.100个线程去抢GIL锁,即抢执行权限
    3      #2. 肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire()
    4      #3. 极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL
    5     #4.直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程
    GIL锁与互斥锁综合分析(重点!!!)

     练习:

     1 from threading import Thread,Lock
     2 import time
     3 n=100
     4 def work():
     5     mutex.acquire()
     6     global n
     7     temp=n
     8     time.sleep(0.1)
     9     n=temp-1
    10     mutex.release()
    11 
    12 if __name__ == '__main__':
    13     mutex=Lock()
    14     t_l=[]
    15     for i in range(100):
    16         t=Thread(target=work)
    17         t_l.append(t)
    18         t.start()
    19     for t in t_l:
    20         t.join()
    21     print(n)
    View Code

      

        

  • 相关阅读:
    【干货】整理分布式技术框架常用的算法及策略
    模块(类)之间解耦利器:EventPublishSubscribeUtils 事件发布订阅工具类
    SqlDapperEasyUtil:.NET CORE下的Dapper封装操作类
    JAVA并发同步互斥实现方式总结
    Elasticsearch必知必会的干货知识一:ES索引文档的CRUD
    整理在Spring IOC容器初始化后可以处理特定逻辑的多种实现方式
    C#编写了一个基于Lucene.Net的搜索引擎查询通用工具类:SearchEngineUtil
    干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结
    .NET CORE与Spring Boot编写控制台程序应有的优雅姿势
    分享基于.NET动态编译&Newtonsoft.Json封装实现JSON转换器(JsonConverter)原理及JSON操作技巧
  • 原文地址:https://www.cnblogs.com/liuwei0824/p/7444848.html
Copyright © 2020-2023  润新知