• jQuery火箭图标返回顶部代码


    线程,进程和协程

    线程

    线程的概念

    并发

    任务数大于cpu核载,通过系统的各种任务跳读算法,是任务“在一起”执行任务! 假的多任务

    并行

    任务数小于cpu核数,即任务真的在一起执行

    多线程

    1 同时执行

    下面例子中test1和test2是同时执行

    import threading
    import time
    
    def tes1():
        for i in range(3):
            print("--test1--%d" % i)
            time.sleep(1)
            
    def tes2():
        for i in range(3):
            print("--test2--%d" % i)
            time.sleep(1)
            
    if __name__ == "__main__":
        t1 = threading.Thread(target=test1)
        t2 = threading.Thread(target=test2)
        
        t1.start()  # 启动线程,让线程开始执行
        t2.start()
    # 执行结果
    --test1--0
    --test2--0
    (间隔1秒)
    --test1--1
    --test2--1
    (间隔1秒)
    --test1--2
    --test2--2

    2 顺序执行

    test1先执行,test2后执行

    import threading
    import time
    
    def tes1():
        for i in range(3):
            print("--test1--%d" % i)
            
    def tes2():
        for i in range(3):
            print("--test2--%d" % i)
            
    if __name__ == "__main__":
        t1 = threading.Thread(target=test1)
        t2 = threading.Thread(target=test2)
        
        t1.start()
        time.sleep(1)
        print("test1 is over")
        
        t2.start()
        time.sleep(1)
        print("test2 is over")
        
    # 执行结果
    --test1--0
    --test1--1
    --test1--2
    (间隔1秒)
    test1 over
    --test2--0
    --test2--1
    --test2--2
    (间隔1面)
    test2 over

    多线程全局变量

    全局变量

    import threading
    import time
    
    g_num = 100
    
    def test1():
        global g_num
        g_num += 1
        print("--in test1 g_num=%d" % g_num)
        
    def test2():
        print("--in test2 g_num=%d" % g_num)
        
    if __name__ == "__main__":
        t1 = threading.Thread(target=test1)
        t2 = threading.Thread(target=test2)
        
        t1.start()
        time.sleep(1)
        
        t2.start()+
        time.sleep(1)

    去函数那个笔记了解全局变量

    多线程全局变量实参

    import threading
    import time
    
    g_num = 100
    
    def test1(temp): #传递实参的方式
        temp.append(33)
        print("--in test1 temp=%s" % str(temp))
        545.
    def test2():
        print("--in test2 temp=%s" % str(temp))
    g_num = [11,22]
    
    if __name__ == "__main__":
        # 创建线程
        t1 = threading.Thread(target=test1, args=(g_num,)) 
        t2 = threading.Thread(target=test2, args=(g_num,))
        
        t1.start()
        time.sleep(1)
        
        t2.start()
        time.sleep(1)

    多线程共享全局变量资源竞争

    import threading
    import time
    
    g_num = 0
    
    def test1():
        glibal g_num
        for i in range(num):
            g_num += 1
        print("--in test1 g_num=%d" % g_num)
        
    def test2():
        glibal g_num
        for i in range(num):
            g_num += 1
        print("--in test2 g_num=%d" % g_num)
        
    if __name__ == "__main__":
        # 创建线程
        t1 = threading.Thread(target=test1,args=(100,))
        t2 = threading.Thread(target=test2,args=(100,))
        
        # t1 = threading.Thread(target=test1,args=(10000,))
        # t2 = threading.Thread(target=test2,args=(10000,))
        
        t1.start()
        t2.start()

    多线程解决全局变量资源竞争01

    使用互斥锁

    import threading
    import time
    
    g_num = 0
    
    def test1():
        glibal g_num
        # 上锁,如果之前没有上锁,那么此时上锁成功
        # 如果之前上过锁,那么就会堵塞在这里,知道这个锁解开为止
        muext.acquire()
        for i in range(num):
            g_num += 1    
        # 解锁
        muext.release()  
        print("--in test1 g_num=%d" % g_num)
                                       
    def test2():
        glibal g_num
        muext.acquire()
        for i in range(num):
            g_num += 1
        muext.release()
        print("--in test2 g_num=%d" % g_num)
    # 建立一个互斥锁,默认是没有上锁的
    muext = threading.Lock()    
    
    if __name__ == "__main__":
        
        t1 = threading.Thread(target=test1,args=(10000,))
        t2 = threading.Thread(target=test2,args=(10000,))
        
        t1.start()
        t2.start()
    # 上述代码上锁和解锁在for循环外,解释:子线程t1和t2不知道谁先执行,假如是t1先执行,在上锁的后,一直执行for循环,知道循环结束为止然后在解锁,在执行t2此时全局变量从0变成了10000,直到for循环结束解锁变成2000,程序解锁

    多线程解决全局变量资源竞争02

    import threading
    import time
    
    g_num = 0
    
    def test1():
        glibal g_num
        for i in range(num):
            muext.acquire()
            g_num += 1
            muext.release()
        print("--in test1 g_num=%d" % g_num)
        
    def test2():
        glibal g_num
        for i in range(num):
            muext.acquire()
            g_num += 1
            muext.release()
        print("--in test2 g_num=%d" % g_num)
    
    muext = threading.Lock()
    
    if __name__ == "__main__":
    
        t1 = threading.Thread(target=test1,args=(10000,))
        t2 = threading.Thread(target=test2,args=(10000,))
        
        t1.start()
        t2.start()
    # 如果上锁和解锁在for循环内部,不管t1和t2谁先执行,每次执行+1结束后解锁,然后在分配t1和t2谁先执行,这个先后是没有规律的,可能是t1执行很多次之后再是执行t2,可能反之,所以其中一个是for循环执行结束后得到20000,但是另外一个一定是执行10000后还在叠加

    多任务版UDP聊天

    import socket
    import threading
    
    if __name__ == "__main__":
        # 创建套接字
        udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # 绑定端口
        udp_socket.bind(("", 7788))
        # 获得对方ip和port
        dest_ip = input("请输入ip:")
        dest_port = input("请输入port:")
        # 接受数据
        while True:
            recv_data = udp_socket.recvfrom(1024)
            print(recv_data)
            
        # 发送数据
        while True:
            send_data = input("输入数据:")
            udp_socket.sendto(send_data.encode("utf-8"), dest_ip,dest_port)
            
    # 利用多线程
    
    import socket
    import threading
    
    def recv_msg(udp_socket):
        # 接受数据
        while True:
            recv_data = udp_socket.recvfrom(1024)
            #print(recv_data)
            print("[%s]:%s" %(recv_data[1], str(recv_data[0].decode("utf-8"))))
            # 显示发送的对方地址和信息
    
    def send_msg(udp_socket,dest_ip,dest_port):
        # 发送数据
        while True:
            send_data = input("输入数据:")
            udp_socket.sendto(send_data.encode("utf-8"), dest_ip,dest_port)
    
    
    if __name__ == "__main__":
        # 创建套接字
        udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # 绑定端口
        udp_socket.bind(("", 7788))
        # 获得对方ip和port
        dest_ip = input("请输入ip:")
        dest_port = int(input("请输入port:"))  # 注意
        
        t_recv = threading.Thread(target=recv_msg, args=(udp_socket,))
        t_send = threading.Thread(target=send_msg, args=(udp_socket,dest_ip,dest_port))
        
        t_recv.start()
        t_send.start()

    进程

    进程的概念

    一个程序运行起来,代码+用到的资源称之为进程,他是操作系统分配资源的基本单位元

    导入multiprocessing模块

    子进程的传递参数

    import multiprocessing
    # import os  # 导入路径模块
    import time
    
    def test(a,b,c,*args, **kwargs):
        print(a)
        print(b)
        print(c)
        print(args)  # 拆包(元组)
        print(kwargs)  # 拆包(字典)
    
    if __name__ == "__main__":
        p = multiprocessing.Process(target=test, args=(1,2,3,4,5,6,7,8), kwargs={"name":"Lily"})
        # 当导入元素和元组的时候,统一以元组的形式导入
        # 当传入kwargs的时候,在创建p类的时候,传入字典形式
        
        p.start()

    多进程之间不共享全局变量

    进程之间是两个独立的程序不共享全局变量

    import multiprocessing
    import time
    
    nums = [1,2,3]  #设定全局变量
    
    def test1():
        nums.append(4)  # 利用方法改变全局变量
        print("在test1中nums=%s" % str(nums))
        
    def test2():
        print("在test2中nums=%s" % str(nums))
        
    if __name__ == "__main__":
        # 创建进程
        p1 = multiprocessing.Proess(target=test1)
        p2 = multiprocessing.Proess(target=test2)
        
        p1.start()
        p1.join()  # 确保p1在执行结束后再执行p2
        time.sleep(1)  # 或者是在p1执行后停顿1秒
        p2.start()
        
    # 得出结果是test1中[1,2,3,4], test2中[1,2,3]
    # 在进程中不共享全局变量

    进程和线程的区别

    简单的对比

    进程:能够完成多任务,例如一台电脑上可以运行多个QQ

    (进程是系统进行资源的分配和调度的一个独立单位)

    线程:能够完成多任务,例如在同一个QQ可以开多个聊天窗口

    (线程属于进程,是进程的一个实体,是cpu调度和分配的基本单位,能更小的独立运行的基本单位,线程自己使用系统资源)

    区别

    1 一个程序至少有一个进程,一个进程至少有一个线程

    2 线程的划分尺度小于进程(就是占用资源比进程少)

    3 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,提高工作效率

    4 线程不能独立的运行,必须依存在进程中

    5 进程就好比工厂的流水线,线程就好比流水线的员工

    进程之间的通信-Queue队列

    实例(仅限一台电脑或是服务器中的两个进程之间的数据共享)

    import multiprocessing
    # 一个进程向Queue中写入数据,另一个进程从Queue获取数据
    # 通过Queue完成了多歌需要配合进程间的数据共享
    
    def down(q):
        # 下载数据
        # 模拟在网上下载数据,就简单的创建下载好的列表
        data = [1,2,3,4]
        
        # 向队列写入数据
        for temp in data:
            q.put(temp)
        print("--数据已经存到队列中---")
        
    def fenxi(q):
        # 数据处理
        fenxi_data = list()  #或者是fenxi_list = []
        
        # 向保存好的队列中获取数据
        while True:
            new_data = q.get()  # new_data是获取的数据
            fenxi_data.append(new_data)
            
            # 判断:如果队被取空的话,就退出
            if q.empty():
                break
    
    if __name__ == "__main__":
        # 创建一个队列
        q = multiprocessing.Queue()
        
        # 创建进程  # 传入实参队列在创建的子进程中调用q
        p1 = multiprocessing.Process(target=down, args=(q,))
        p2 = muultiprocessing.Proess(target=fenxi, args=(q,))
        
        p1.start()
        # p1.join()
        p2.start()
    
    # 以上操作有问题,创建的子进程p1和p2,不能确定那个子进程先运行,会导致p1还没有下载好,p2就直接获取数据,所以在p1.start()后添加一个p1.join(), p1.join()的功能就是让p1执行完之后再执行其他的进程

    补充说明

    初始化Queue()对象时(例如q=Queue()),有下列方法

    q.put() # 向队列导入写入数据

    q.get() # 向队列下载获取数据

    q.empty() # 如果队列是空的,返回True, 反之False

    q.full() # 如果列队满了,返回True,反之False

    进程池

    进程之间的通信

    进程间通信就是在不同进程之间传播或交换信息,那么不同进程之间存在着什么双方都可以访问的介质呢?进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。但是,系统空间却是“公共场所”,所以内核显然可以提供这样的条件。除此以外,那就是双方都可以访问的外设了。在这个意义上,两个进程当然也可以通过磁盘上的普通文件交换信息,或者通过“注册表”或其它数据库中的某些表项和记录交换信息。广义上这也是进程间通信的手段,但是一般都不把这算作“进程间通信”。因为那些通信手段的效率太低了,而人们对进程间通信的要求是要有一定的实时性。

    进程间通信主要包括管道, 系统IPC(包括消息队列,信号量,共享存储), SOCKET.

    进程和线程区别

    定义的区别:进程是系统进行资源分配和调度的一个独立的单位;线程是进程的实体,是cpu调度和分配的基本单位

    一个程序至少有一个进程,一个进程至少有一个线程;

    线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。

    进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率

    线线程不能够独立执行,必须依存在进程中

    可以将进程理解为工厂中的一条流水线,而其中的线程就是这个流水线上的工人

    优缺点:线程执行开销小,效率高,,但不利于资源的管理和保护;而进程正相反。

    协程

    协程概念

    用到更少的资源,:在一个线程中的某个函数,可以在任何地方保存当前函数的一 些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函 数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开 发者自己确定

    协程和线程的区别

    在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简 单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数 据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性 能。但是协程的切换只是单纯的操作CPU的上下,所以⼀秒钟切换个上百 万次系统都抗的住。

    如何实现协程

    1 生成器实现简单的协程

    import time 
    
    def f1():
        while True:
            print("---1----")
            yield
            time.sleep(1)
    
    def f2():
        while True:
            print("---2----")
            yield
            time.sleep(1)
            
    if __name__ == "__main__":
        ff1 = f1()
        ff2 = f2()
        while True:
            next(ff1)
            next(ff2)

    2 greenlet实现协程 (了解)

    from greenlet import greenlet
    import time
    
    def w1():
        while True:
            print("---1---")
            ww1.switch()  # 使用greenlet模块中swtich()方法
            time.sleep(1)
            
    def w2():
        while True:
            print("---1---")
            ww2.switch()  # 使用greenlet模块中swtich()方法
            time.sleep(1)
            
    if __name__ == "__main__":
        ww1 = greenlet(w1)
        ww2 = greenlet(w2)
        ww1.switch()

    3 gevent实现协程(重要)

    import gevent
    
    def f(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
    
    g1 = gevent.spawn(f,5)
    g2 = gevent.spawn(f,5)
    g3 = gevent.spawn(f,5)
    
    g1.join()
    g2.join()
    g3.join()
    
    # 执行结果
    <Greenlet "Greenlet-0" at 0x7f9e6251a748: f(5)> 0
    <Greenlet "Greenlet-0" at 0x7f9e6251a748: f(5)> 1
    <Greenlet "Greenlet-0" at 0x7f9e6251a748: f(5)> 2
    <Greenlet "Greenlet-0" at 0x7f9e6251a748: f(5)> 3
    <Greenlet "Greenlet-0" at 0x7f9e6251a748: f(5)> 4
    <Greenlet "Greenlet-1" at 0x7f9e6251a948: f(5)> 0
    <Greenlet "Greenlet-1" at 0x7f9e6251a948: f(5)> 1
    <Greenlet "Greenlet-1" at 0x7f9e6251a948: f(5)> 2
    <Greenlet "Greenlet-1" at 0x7f9e6251a948: f(5)> 3
    <Greenlet "Greenlet-1" at 0x7f9e6251a948: f(5)> 4
    <Greenlet "Greenlet-2" at 0x7f9e6251aa48: f(5)> 0
    <Greenlet "Greenlet-2" at 0x7f9e6251aa48: f(5)> 1
    <Greenlet "Greenlet-2" at 0x7f9e6251aa48: f(5)> 2
    <Greenlet "Greenlet-2" at 0x7f9e6251aa48: f(5)> 3
    <Greenlet "Greenlet-2" at 0x7f9e6251aa48: f(5)> 4
    
    
    
    # 达成任务切换
    import gevent
    
    def f(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            # 用来模拟一个耗时操作,不是用time模块
            gevent.sleep(1)
    
    g1 = gevent.spawn(f,5)
    g2 = gevent.spawn(f,5)
    g3 = gevent.spawn(f,5)
    
    g1.join()
    g2.join()
    g3.join()
    
    # 执行结果
    <Greenlet "Greenlet-0" at 0x7fc93b84d748: f(5)> 0
    <Greenlet "Greenlet-1" at 0x7fc93b84d948: f(5)> 0
    <Greenlet "Greenlet-2" at 0x7fc93b84da48: f(5)> 0
    <Greenlet "Greenlet-0" at 0x7fc93b84d748: f(5)> 1
    <Greenlet "Greenlet-1" at 0x7fc93b84d948: f(5)> 1
    <Greenlet "Greenlet-2" at 0x7fc93b84da48: f(5)> 1
    <Greenlet "Greenlet-0" at 0x7fc93b84d748: f(5)> 2
    <Greenlet "Greenlet-1" at 0x7fc93b84d948: f(5)> 2
    <Greenlet "Greenlet-2" at 0x7fc93b84da48: f(5)> 2
    <Greenlet "Greenlet-0" at 0x7fc93b84d748: f(5)> 3
    <Greenlet "Greenlet-1" at 0x7fc93b84d948: f(5)> 3
    <Greenlet "Greenlet-2" at 0x7fc93b84da48: f(5)> 3
    <Greenlet "Greenlet-0" at 0x7fc93b84d748: f(5)> 4
    <Greenlet "Greenlet-1" at 0x7fc93b84d948: f(5)> 4
    <Greenlet "Greenlet-2" at 0x7fc93b84da48: f(5)> 4

    进程,线程和协程的对比(面试重点)

    A 进程是资源分配的单位

    B 线程是操作系统调度的单位

    C 进程切换需要的资源最大,效率低

    D 线程切换需要的资源一般,效率也很一般

    E 协程切换任务资源小,效率高

    F 多进程,多线程根据cpu的核数不一样可能并行,但是协程是在一个线程中,所以是并发的

    G 进程不共享资源,线程共享资源

    GIL(全局解释器锁)(面试重点)

    每个线程在执行过程中都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,所以线程是并发的,都是讲并发运行成串行,由此来控制同一时间内共享数据只能被一个任务修改,进而保证数据的安全!

    底层知识

    因为python的线程是调用操作系统的原生线程,这个原生线程就是C语言写的原生线程。因为python是用C写的,启动的时候就是调用的C语言的接口。因为启动的C语言的远程线程,那它要调这个线程去执行任务就必须知道上下文,所以python要去调C语言的接口的线程,必须要把这个上限问关系传给python,那就变成了一个在加减的时候要让程序串行才能一次计算。就是先让线程1,再让线程2.......

    多线程用于IO密集型,如socket,爬虫,web

    多进程用于计算密集型,如金融分析

    互斥锁(面试重点)

    当多个线程几乎同时修改一个共享数据的时候,需要进行同步控制,线程同步能够保证多个线程安全的竞争资源,最简单的同步机制就是引入互斥锁

    如何运行:某个线程需要更改共享数据的时候,先锁定,此时资源状态为锁定状态,其他线程不能更改,直到该线程释放资源,将资源的状态变成非锁定状态,其他线程才能再次锁定该资源,互斥锁保证每次只有一个线程进行操作,从而保证多线程情况的数据正确性!

    优点:确保某段关键代码只能有一个线程从头到尾完整的执行

    缺点:A--阻止了多线程的并发,包含锁的某段代码只能以单线程的模式执行,效率大打折扣。B--由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方的锁,可能会造成死锁!

  • 相关阅读:
    8张图带你轻松温习 Java 知识.md
    关于 Java 序列化你不知道的 5 件事
    为什么 String 是不可变的?
    如何使用 Nginx 优雅地限流?
    JAVA泛型编程笔记
    java_接口的应用
    java_抽象类应用
    深入理解Java的接口和抽象类
    java_重写与重载的区别
    Java:按值传递还是按引用传递详细解说
  • 原文地址:https://www.cnblogs.com/MayDayTime/p/10186959.html
Copyright © 2020-2023  润新知