zoukankan      html  css  js  c++  java
  • python网络线程

    线程

    线程是一种多任务编程的方式,可以使用计算机多核资源。线程又被称为轻量级的进程

    线程特征

      * 线程是计算机核心分配的最小单位
      * 一个进程可以包含多个线程
      * 线程也是一个运行过程,也要消耗计算机资源。多个线程共享其进程的资源和空间
      * 线程也拥有自己特有的资源属性,比如指令集,TID等
      * 线程无论创建还是删除还是运行资源消耗都小于进程
      * 多个线程之间并行执行,互不干扰

    threading线程模块

    from threading import Thread

    t = Thread(target, [, args], [kwargs])

    创建线程对象

    • target 绑定线程函数
    • args 元组 给线程函数位置传参
    • kwargs 字典 给线程函数键值传参

    t.start()        启动线程

    t.join([timeout])   回收线程

    import threading
    import os
    
    a = 1
    
    # 线程函数
    def music():
      print("进程pid号", os.getpid())
      global a
      print("a = ",a)
      a = 10000
    
    t = threading.Thread(target=music)    # 创建线程对象
    t.start()                               # 启动线程
    
    print("进程pid号", os.getpid())
    
    t.join()                                # 回收线程
    
    print("Main a:",a)
    
    # 进程pid号 12549
    # 进程pid号 12549
    # a =  1
    # Main a: 10000

     os.getpid获取的是进程的pid号,线程是进程中的一个成员.

    线程中改的变量,是进程中的变量.并没有新开辟一个空间.

    线程属性

    t.is_alive()  查看线程状态

    t.name  线程名称  默认Thread-1

    t.setName()  设置线程名称

    threading.currentThread()  获取当前线程对象

     1 from threading import Thread,currentThread
     2 from time import sleep 
     3 
     4 #线程函数
     5 def fun(sec):
     6     print("线程属性测试")
     7     sleep(sec)
     8     #获取线程对象 getName()获取名字
     9     print("%s 线程结束"%currentThread().getName())
    10 
    11 thread = []
    12 
    13 for i in range(3):
    14     t = Thread(target = fun,name = "tedu%d"%i,
    15         args = (3,))
    16     thread.append(t)
    17     t.start()
    18     print(t.is_alive())  #查看进程状态
    19 
    20 thread[1].setName('Tarena')  #设置线程名称
    21 print(thread[2].name) #获取线程名称
    22 
    23 #回收线程
    24 for i in thread:
    25     i.join()
    26 
    27 # 线程属性测试
    28 # True
    29 # 线程属性测试
    30 # True
    31 # 线程属性测试
    32 # True
    33 # tedu2
    34 # Tarena 线程结束
    35 # tedu0 线程结束
    36 # tedu2 线程结束
    View Code

    t.daemon

    默认情况下,主线程的结束不会影响分支线程,如果设置为True则主线程退出分支线程也会退出

    设置方法:

    t.daemon = True

    t.setDaemon()

    线程daemon属性的设置在start前;一般设置daemon后不会使用join

    from threading import Thread
    from time import sleep
    
    def fun():
      sleep(3)
      print("线程属性测试")
    
    t = Thread(target=fun, name = "Tarena")
    
    # 主线程退出分支线程也退出
    t.setDaemon(True)
    
    t.start()
    
    t.setName("Tedu")
    print("Name:",t.getName())  # 线程名称
    print("Alive:",t.is_alive())  # 线程生命周期
    print("is Daemon",t.isDaemon()) # 主进程随着分支进程退出

    自定义线程类

    1. 继承Thread类
    2. 运行Thread类中的__init__方法以获取父类属性
    3. 重写run方法

    使用方法

    1. 实例化对象
    2. 调用start自动化执行run方法
    3. 调用join回收线程
    from threading import Thread
    
    class ThreadClass(Thread):
      # 重写父类init
      def __init__(self, *args, **kwargs):
        self.attr = args[0]
        super().__init__()    # 加载父类init
    
      def fun1(self):
        print("函数1")
    
      def fun2(self):
        print("函数2")
    
      # 重写run,逻辑调用
      def run(self):
        self.fun1()
        self.fun2()
    
    
    t = ThreadClass("abc")
    t.start()
    t.join()
    
    # 函数1
    # 函数2

    同步互斥

    线程间通信方法

    1.通信方法:线程间使用全局变量进行通信

    2. 共享资源争夺

    • 共享资源:多个进程或者线程都可以操作的资源称为共享资源。对共享资源的操作代码段称为临界区。
    • 影响 :对共享资源的无序操作可能会带来数据的混乱,或者操作错误。此时往往需要同步互斥机制协调操作顺序。

    3. 同步互斥机制

    同步 : 同步是一种协作关系,为完成操作,多进程或者线程间形成一种协调,按照必要的步骤有序执行操作。

    互斥 : 互斥是一种制约关系,当一个进程或者线程占有资源时会进行加锁处理,此时其他进程线程就无法操作该资源,直到解锁后才能操作。

    线程同步互斥方法

    线程Event

    from threading import Event

    e = Event()      创建线程event对象

    e.wait([timeout])    阻塞等待e被set

    e.set()        设置e,使wait结束阻塞

    e.clear()        使e回到未被设置状态

    e.is_set()       查看当前e是否被设置

    from threading import Thread,Event
    
    s = None  # 用于通信
    e = Event() # 创建event对象
    
    def 杨子荣():
      print("杨子荣前来拜山头")
      global s
      s = "天王盖地虎"
      e.set()  # 对e设置
    
    t = Thread(target=杨子荣)
    t.start()
    
    print("说对口令就是自己人")
    e.wait()  # 阻塞等待口令说出
    if s == '天王盖地虎':
      print("宝塔镇河妖")
      print("确认过眼神,你是对的人")
    else:
      print("打死他...")
    
    
    t.join()

    线程锁 Lock

    from threading import Lock

    lock = Lock()   创建锁对象

    lock.acquire()   上锁 如果lock已经上锁再调用会阻塞

    lock.release()   解锁

    with lock:    # 上锁

    ...

    ...

          with代码块结束自动解锁

    from threading import Thread,Lock
    
    a = b = 0
    lock = Lock() # 定义锁
    
    def value():
      while True:
        lock.acquire()  # 上锁
        if a != b:
          print("a = %d,b = %d"%(a,b))
        lock.release() # 解锁
    
    t = Thread(target = value)
    t.start()
    
    while True:   # 上锁
      with lock:
        a += 1
        b += 1
                  # 自动解锁
    t.join()

    python线程的GIL问题

    GIL (全局解释器锁)

    python ---》 支持线程操作 ---》IO的同步和互斥 --》 加锁 ----》 超级锁,给解释器加锁

    后果:一个解释器,同一时刻只解释一个线程,此时其他线程需要等待。大大降低了python线程的执行效率

    python GIL问题解决方案
    * 修改c解释器
    * 尽量使用多进程进行并行操作
    * python线程可以用在高延迟多阻塞的IO情形
    * 不使用cpython  c# java做解释器

    效率测试

    分别测试 多进程 多线程 单进程执行相同的IO操作和CPU

    #计算密集
    def count(x,y):
        c = 0
        while c < 7000000:
            x += 1
            y += 1
            c += 1
    
    #io密集
    def write():
        f = open("test.txt",'w')
        for x in range(2000000):
            f.write("hello world
    ")
        f.close()
    
    def read():
        f = open("test.txt")
        lines = f.readlines()
        f.close()
    View Code

    操作的时间

    #单进程程序
    from test import *
    import time 
    
    # t = time.time()
    # for i in range(10):
    #     count(1,1)
    # print("Line cpu:",time.time() - t)
    
    
    t = time.time()
    for i in range(10):
        write()
        read()
    print("Line IO:",time.time() - t)
    View Code

    Line cpu: 8.15166711807251
    Line IO: 6.841825246810913

    from test import * 
    import threading 
    import time 
    
    counts = []
    
    t = time.time()
    
    for x in range(10):
        th = threading.Thread(target = count,args = (1,1))
        th.start()
        counts.append(th)
    
    for i in counts:
        i.join()
    print("Thread cpu",time.time() - t)
    View Code
    from test import * 
    import threading 
    import time 
    
    counts = []
    
    def io():
        write()
        read()
    
    t = time.time()
    
    for x in range(10):
        th = threading.Thread(target = io)
        th.start()
        counts.append(th)
    
    for i in counts:
        i.join()
    print("Thread IO",time.time() - t)
    View Code

    Thread cpu 8.414522647857666
    Thread IO 6.023292541503906

    from test import * 
    import multiprocessing 
    import time 
    
    counts = []
    
    t = time.time()
    
    for x in range(10):
        th = multiprocessing.Process
        (target = count,args = (1,1))
        th.start()
        counts.append(th)
    
    for i in counts:
        i.join()
    print("Process cpu",time.time() - t)
    View Code
    from test import * 
    import multiprocessing 
    import time 
    
    counts = []
    
    def io():
        write()
        read()
    
    t = time.time()
    
    for x in range(10):
        th = multiprocessing.Process(target = io)
        th.start()
        counts.append(th)
    
    for i in counts:
        i.join()
    print("Process IO",time.time() - t)
    View Code

    Process cpu 4.079084157943726
    Process IO 3.2132551670074463

    进程和线程的区别和联系

    1. 两者都是多任务编程的方式,都能够使用计算机的多核
    2. 进程的创建删除要比线程消耗更多的计算机资源
    3. 进程空间独立,数据安全性好,有专门的进程间通信方法
    4. 线程使用全局变量通信,更加简单,但是需要同步互斥操 作
    5.  一个进程可以包含多个线程,线程共享进程的空间资源
    6.  进程线程都独立执行,有自己的特有资源如属性,id, 命令集等

    使用情况:

    • 一个进程中并发任务比较多,比较简单,适合使用多线程
    • 如果数据程序比较复杂,特别是可能多个任务通信比较多 的时候,要考虑到使用线程同步互斥的复杂性
    • 多个任务存在明显差异,和功能分离的时候没有必要一定 写入到一个进程中
    • 使用python考虑线程GIL问题
  • 相关阅读:
    SQL Server 2008数据库镜像+故障转移集群原理摘自网络
    AHCI和IDE区别,和在目前系统中设置 AHCI 摘自网络
    中国计算机技术职业资格网(软考 官方 官网)
    第十一节 导出Excel 简单
    第五章 IF语句与逻辑运算符 简单
    第十一节 10图片权限控制 简单
    第四章 C++数据类型 简单
    第二章 从一个最简短的C++程 简单
    php 解析xml文件 简单
    第十一节 6HttpHandler案例 简单
  • 原文地址:https://www.cnblogs.com/LXP-Never/p/9474024.html
Copyright © 2011-2022 走看看