python之网络编程-多线程

多线程

  1. 线程的理论知识

    1. 什么是线程

      当开启一个进程的时候:内存中开辟空间,加载资源与数据,调用CPU执行,可能还会使用这个空间的资源。

      定义:每个进程都有一个地址空间,而且默认就有一个控制线程。进程只是把资源集中到一起(进程可以认为是一个含有代码的空间),而线程才是CPU的执行单位。

      多线程:在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间。

    2. 线程vs进程
      • 开启多进程开销大,开启线程开销非常小
      • 开启多进程的速度慢,开启多线程速度快
      • 进程之间数据不能直接共享(通过队列可以),同一个进程下的线程之间的数据可以共享。

      总结:进程:划分空间,加载资源,静态的;线程:执行代码,执行能力,动态的

    3. 多线程的应用场景

      并发:一个CPU来回切换(线程间的切换)

      多进程并发:开启多个进程,每个进程里面的主进程执行任务

      多线程并发:开启1个(或多个)进程,每个进程里面多个线程执行任务

      当一个程序中包含三个不同的任务时,就可以使用多线程,由于每个线程之间可以直接共享数据。

  2. 开启线程的两种方式

    与开启多进程相似,只是引用模块为threading。

    • 方式一:

      from threading import Thread
      def task():
          print('打印子线程')
      if __name__ == '__main__':
          t = Thread(target=task)
          t.start()
          print('主线程')
      # 打印子线程
      # 主线程
    • 方式二
      from threading import Thread
      class MyThread(Thread):
          def run(self):
              print('打印子线程')
      if __name__ == '__main__':
          t = MyThread()
          t.start()
          print('主线程')
      # 打印子线程
      # 主线程
  3. 线程与进程对比
    1. 速度对比

      开启进程的速度比开启线程的速度慢得多

    2. pid

      线程的pid就是所属进程的pid

    3. 线程之间共享数据资源,进程之间理论上时隔离的
    4. 不同的进程直接就是竞争关系;而同一个进程的线程之间时合作关系

    为什么要用多线程?(如果多个任务共用一个地址空间那么必须在一个进程内开启多个线程)

    • 多线程共享一个进程的地址空间
    • 线程比进程更轻量级,线程更容易撤销,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用
    • 若多个线程都是cpu密集型的那么并不能获得性能上的增强,但如果存在大量计算和大量的i/o处理,拥有多个线程允许这些活动彼此重叠运行,从而加快程序执行的速度
    • 再多CPU系统中,为了最大限度的利用多核,可以开启多个线程,比开进程开销小的多(并不适合python)
  4. 线程的其他方法
    # Thread实例对象的方法:
    isAlive()    # 判断线程是否是活动的
    getName()    # 返回线程名
    setName()    # 设置线程名
    # threading模块提供的一些方法:
    threading.currentThread()    # 返回当前的线程变量
    threading.enumerate()    # 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程
    threading.activeCount()  # 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果
  5. 守护线程

    无论是进程还是线程,都遵循守护线程(或进程)会等待主线程(或进程)结束后被终止。

    注意:

    • 对于主进程来说,运行结束指的是主进程代码运行完毕

      主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束

    • 对于主线程来说,运行结束指的是主线程所在的进程内的所有非守护线程全都运行完毕,主线程才算结束。

      主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束

    from threading import Thread
    import time
    def task():
        time.sleep(1)
        print('打印task')
    
    def task1():
        time.sleep(3)
        print('打印task1')
    if __name__ == '__main__':
        t1 = Thread(target=task)
        t2 = Thread(target=task1)
        print('主进程') # 必须等到task1执行完,主进程才能结束
    # 主进程
    # 打印task
    # 打印task1
  6. 互斥锁

    多线程的同步锁与多进程的同步锁是一个道理,就是多个线程抢占同一个数据(资源)时,我们要保证数据的安全,合理的顺序。

    # 不加锁抢占同一资源
    from threading import Thread
    import time
    x = 100
    
    def task():
        global x
        temp = x
        time.sleep(1)
        temp -= 1
        x = temp
    
    if __name__ == '__main__':
        t_l = []
        for i in range(100):
            t = Thread(target=task)
            t_l.append(t)
            t.start()
        for i in t_l:
            i.join()
        print(f'主线程{x}')
    # 主线程99
    '''
    所有的线程同一时间拿到的x都是100,执行结束全都是99
    '''

    加锁保证了数据安全

    from threading import Thread
    from threading import Lock
    import time
    x = 100
    def task(lock):
        lock.acquire()
        global x
        temp = x
        time.sleep(0.1)
        temp -= 1
        x = temp
        lock.release()
    if __name__ == '__main__':
        t_l = []
        lock = Lock()
        for i in range(100):
            t = Thread(target=task,args=(lock,))
            t_l.append(t)
            t.start()
        for i in t_l:
            i.join()
        print(f'主线程{x}')
    # 主进程0

    互斥锁与join的区别?

    互斥锁是随机强锁,公平的

    join是提前安排好顺序,虽然是串行,但不公平

  7. 死锁现象,递归锁

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

    from threading import Thread
    from threading import Lock
    import time
    lock_A = Lock()
    lock_B = Lock()
    class MyThread(Thread):
        def run(self):
            self.f1()
            self.f2()
        def f1(self):
            lock_A.acquire()
            print(f'{self.name}拿到了锁A')
            lock_B.acquire()
            print(f'{self.name}拿到了锁B')
            lock_B.release()
            lock_A.release()
        def f2(self):
            lock_B.acquire()
            print(f'{self.name}拿到了锁B')
            time.sleep(1)
            lock_A.acquire()
            print(f'{self.name}拿到了锁A')
            lock_A.release()
            lock_B.release()
    if __name__ == '__main__':
        for i in range(1,4):
            t = MyThread()
            t.start()
    # Thread-1拿到了锁A
    # Thread-1拿到了锁B
    # Thread-1拿到了锁B
    # Thread-2拿到了锁A
    # 锁死了....

    遇到死锁现象可通过 递归锁解决。

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

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

    from threading import Thread
    from threading import RLock
    import time
    lock_A = RLock()
    class MyThread(Thread):
        def run(self):
            self.f1()
            self.f2()
        def f1(self):
            lock_A.acquire()
            print(f'{self.name}拿到了锁A')
            lock_A.acquire()
            print(f'{self.name}拿到了锁B')
            lock_A.release()
            lock_A.release()
        def f2(self):
            lock_A.acquire()
            print(f'{self.name}拿到了锁B')
            time.sleep(1)
            lock_A.acquire()
            print(f'{self.name}拿到了锁A')
            lock_A.release()
            lock_A.release()
    if __name__ == '__main__':
        for i in range(1,4):
            t = MyThread()
            t.start()
    # Thread-1拿到了锁A
    # Thread-1拿到了锁B
    # Thread-1拿到了锁B
    # Thread-1拿到了锁A
    # Thread-2拿到了锁A
    # Thread-2拿到了锁B
    # Thread-2拿到了锁B
    # Thread-2拿到了锁A
    # Thread-3拿到了锁A
    # Thread-3拿到了锁B
    # Thread-3拿到了锁B
    # Thread-3拿到了锁A
    '''
    递归锁是一把锁,锁上有记录,只要acquire一次,锁上就计数1次, acquire2次,锁上就计数2次,
    release1次,减一,
    只要递归锁计数不为0,其他线程不能抢.
    '''
  8. 信号量Semaphore

    信号量允许多个线程或进程同时进入

    Semaphore管理一个内置计数器,当调用acquire()时内置计数器-1,当调用release()时内置计数器+1,当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

    from threading import Thread
    from threading import current_thread
    from threading import Semaphore
    import time,random
    lock = Semaphore(4)
    def task():
        lock.acquire()
        print(f'{current_thread.name}正在连接')
        time.sleep(random.randint(1,3))
        lock.release()
    if __name__ == '__main__':
        for i in range(10):
            t = Thread(target=task)
            t.start()
    # Thread-1正在连接
    # Thread-2正在连接
    # Thread-3正在连接
    # Thread-4正在连接
    
    # Thread-5正在连接
    # Thread-6正在连接
    
    # Thread-7正在连接
    # Thread-8正在连接
    
    # Thread-9正在连接
    # Thread-10正在连接

原文地址:https://www.cnblogs.com/yaoqi17/p/11247290.html

时间: 2024-08-30 08:00:16

python之网络编程-多线程的相关文章

【Python】网络编程

1.TCP编程 2.SocketServer模块 3.Twisted框架 4.UDP编程 1.TCP编程--TCP是面向连接的,其一般的设计如下: # encoding:utf-8 ''' Created on 2014-6-20 @author: Administrator ''' from socket import socket, AF_INET, SOCK_STREAM import time import threading class SockServer(object): def

python基础网络编程--转

python之网络编程 本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的和具名的) 远程过程调用(Solaris门和Sun RPC) 但这些都不是本文的主题!我们要讨论的是网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的.其实TCP/IP协议族已经帮我们解决了这个问题,网

python ==》 网络编程

 一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层模型 互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层 学习socket一定要先学习互联网协议: 1.首先:本节课程的目标就是教会你如何基于socket编程,来开发一款自己的C/S架构软件 2.其次:C/S架构的软件(软件属于应用层)是基于网络进行通信的 3.然后:网络的核心

python之网络编程

14.1.1 socket模块 在网络编程中德一个基本组件就是套接字.套接字主要是两个程序之间的信息通道. 套接字包括两个:服务器套接字和客户机套接字.创建一个服务器套接字后,让它等待连接.这样它就在某个网络地址处监听. 一个套接字就是一个socket模块中socket类的实例.它的实例化需要3个参数:第一个参数是地址族(默认是socket.AF_INET);第2个参数是流(socket.SOCK_STREAM,默认值)或数据报(socket.SOCK_DGRAM)套接字.第三个参数是使用的协议

Python Socket 网络编程

Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ 聊天.收发 email 等等.要解决网络上两台主机之间的进程通信问题,首先要唯一标识该进程,在 TCP/IP 网络协议中,就是通过 (IP地址,协议,端口号) 三元组来标识进程的,解决了进程标识问题,就有了通信的基础了. 本文主要介绍使用 Python 进行 TCP Socket 网络编程,假设你已

Git的使用及网络编程多线程多进程

Git的使用 1.打开CMD命令行,输入cd Desktop进入桌面 2.输入 mkdir + 'file name'创建文件,如果已有项目则输入 cd + file name进入文件,如果在Git上已有项目并且在已有项目进行修改,则输入cd clone + url 3.如果第一次使用,则先初始化,git init 4. git add + file name 把项目加入文件夹里面 5.git config -global 全局设置(第一次使用的时候设置) 6. git log 查看日志.git

Python 之 网络编程——SOCKET开发

一.预备知识 对于我们,主要掌握5层协议就行. 物理层: 转成二进制数序列数据链路层: 形成统一的协议:Internet协议 包括数据头(18个字节,前6个字节原地址,中间6个字节为目标地址,后6个字节为数据的描述)和数据网络层: 有IP协议,包括IP头和数据传输层: 包括tcp.UDP两个协议:基于端口(0-65535)的协议应用层: 包括http.ftp协议 TCP协议:流式协议,先把管道修好 客户端   服务端 C-------------------------------->S   <

Python学习---网络编程 1217[all]

OSI七层模型: 物理层, 数据链路层, 网络层,传输层,会话层,表达层,应用层 应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等 传输层:TCP,UDP 网络层:IP,ICMP,OSPF,EIGRP,IGMP 数据链路层:SLIP,CSLIP,PPP,MTU [图片来自网络] 网络通信要素: A:IP地址    (1) 用来标识网络上一台独立的主机 (2) IP地址 = 网络地址 + 主机地址(网络号:用于识别主机所在的网络/网段.主机号:用于识别该网络中的主

《Python》网络编程之客户端/服务端框架、套接字(socket)初使用

一.软件开发的机构 我们了解的涉及到两个程序之间通讯的应用大致可以分为两种: 第一种是应用类:QQ.微信.网盘等这一类是属于需要安装的桌面应用 第二种是web类:比如百度.知乎.博客园等使用浏览器访问就可以直接使用的应用 这些应用的本质其实都是两个程序之间的通讯,而这两个分类又对应了两个软件开发的架构 1.C/S架构 C/S即:Client与Server,中卫意思:客户端与服务器端架构,这种架构也是从用户层面(也可以是物理层面)来划分的. 这里的客户端一般泛指客户端应用程序EXE,程序需要先安装