• python之并发编程初级篇8


    一、进程理论

    1)进程介绍

    1、什么是进程
        一个正在进行的过程,或者说是一个程序的运行过程
        其实进程是对正在运行的程序的一种抽象/概括的说法
    
        进程的概念起源操作系统,进程是操作最核心的概念之一
        操作系统其他所有的概念都是围绕进程展开的
    
    2、多道路技术
        产生背景:针对单核,实现并发
        ps:
        现在的主机一般是多核,那么每个核都会利用多道技术
        有4个cpu,运行于cpu1的某个程序遇到io阻塞,会等到io结束再重新调度,会被调度到4个
        cpu中的任意一个,具体由操作系统调度算法决定。
    
        2.空间上的复用:如内存中同时有多道程序
        3.时间上的复用:复用一个cpu的时间片
           强调:遇到io切,占用cpu时间过长也切,核心在于切之前将进程的状态保存下来,这样
                才能保证下次切换回来时,能基于上次切走的位置继续运行
    
    3、并发vs并行
        并发(单核就可以实现并发)
            多个任务看起来是同时运行的就是并发
            并发的实现=切换+保存状态
    
        并行(只有多核才能实现并行)
            真正意义上的同时运行

    2)进程状态

    tail -f access.log |grep '404'
      执行程序tail,开启一个子进程,执行程序grep,开启另外一个子进程,两个进程之间基于管道'|'通讯,将tail的结果作为grep的输入。
      进程grep在等待输入(即I/O)时的状态称为阻塞,此时grep命令都无法运行
      其实在两种情况下会导致一个进程在逻辑上不能运行,
      1. 进程挂起是自身原因,遇到I/O阻塞,便要让出CPU让其他进程去执行,这样保证CPU一直在工作
      2. 与进程无关,是操作系统层面,可能会因为一个进程占用时间过多,或者优先级等原因,而调用其他的进程去使用CPU。
      因而一个进程由三种状态

    3)进程并发的实现

       程并发的实现在于,硬件中断一个正在运行的进程,把此时进程运行的所有状态保存下来,为此,操作系统维护一张表格,即进程表(process table),每个进程占用一个进程表项(这些表项也称为进程控制块)

      该表存放了进程状态的重要信息:程序计数器、堆栈指针、内存分配状况、所有打开文件的状态、帐号和调度信息,以及其他在进程由运行态转为就绪态或阻塞态时,必须保存的信息,从而保证该进程在再次启动时,就像从未被中断过一样。

    Python并发编程理论原文链接:http://www.cnblogs.com/linhaifeng/articles/7430066.html

    二、进程

    1)开启子进程

    from multiprocessing import Process
    import time
    
    def task(x):
        print('%s is running' %x)
        time.sleep(2)
        print("%s is done" %x)
    
    
    if __name__ == '__main__':
        # Process(target=task,kwargs={'x':'任务1'})   # 生成一个进程对象
        p=Process(target=task,args=('任务1',))    # 元组必须加逗号,否则是字符串类型
        p.start() # 只是给操作系统发送一个开启子进程的信号
        print('')
    View Code

    2)查看进程号(进程ID),pid及ppid

    from multiprocessing import Process
    import time
    import os
    
    def task():
        print('子进程的pid:%s,父进程的pid ppid:%s' %(os.getpid(),os.getppid()))
        time.sleep(1)
    
    if __name__ == '__main__':
        p=Process(target=task,)
        p.start()
        print('主:',os.getpid(),os.getppid())
    View Code

    tasklist  查看ID

    tasklist | findstr 11244  过滤ID

    3)使用自定义类方法来创建子进程

    from multiprocessing import Process
    import time
    
    class MyProcess(Process):
        def __init__(self,name):
            super().__init__()
            self.name=name
    
        def run(self):  # 函数名一定得叫run
            print('%s is ruuning' %self.name)
            time.sleep(3)
            print('%s is done' %self.name)
    
        def func(self):
            print('new func')
    
    if __name__ == '__main__':
        p=MyProcess('egon')
        p.start() # 调用类里面的 run 方法,即run 方法乃 子进程
        print('')
    
        p.func()
    View Code

     4)join 方法。必先执行完子进程,才能执行主进程

    from multiprocessing import Process
    import time
    
    def task(x):
        print('%s is ruuning' %x)
        time.sleep(3)
        print('%s is done' %x)
    
    if __name__ == '__main__':
        p=Process(target=task,args=('子进程',))
        p.start()
        # time.sleep(5)
        p.join() #主进程在等,一直等到子进程p运行完毕后再执行下一行代码
        print('')
    View Code

     多个子进程,重用执行的时间,用时 3.6552090644836426秒。会与后面的线程对比

    from multiprocessing import Process
    import time,os
    
    def task(n):
        print('%s is ruuning' %os.getpid())
        time.sleep(n)
    
    
    if __name__ == '__main__':
        p1=Process(target=task,args=(1,))
        p2=Process(target=task,args=(2,))
        p3=Process(target=task,args=(3,))
        start_time=time.time()
        p1.start()
        p2.start()
        p3.start()
    
        p1.join()
        p2.join()
        p3.join()
        stop_time=time.time()
        print('',(stop_time - start_time))     #  主 3.6552090644836426 
    View Code

     循环添加进程,并循环执行

    from multiprocessing import Process
    import time,os
    
    def task(n):
        print('%s is ruuning' %os.getpid())
        time.sleep(n)
    
    if __name__ == '__main__':
        p_l=[]
        start_time=time.time()
        for i in range(1,4):
            p=Process(target=task,args=(i,))
            p_l.append(p)
            p.start()
    
        for p in p_l:
            p.join()
    
        stop_time=time.time()
        print('',(stop_time - start_time))
    View Code

     5)进程的其他方法,属性

    p.name,p.pid        # 打印子进程的名字
    p.pid               # 子进程的pid
    p.terminate()       # 强行终止子进程
    print(p.is_alive()) # 查看子进程是否还活着
    from multiprocessing import Process
    import time,os
    
    def task(n):
        print('%s is ruuning' %os.getpid())
        time.sleep(n)
    
    if __name__ == '__main__':
        p=Process(target=task,args=(3,),name='子进程1')
        p.start()
        # print(p.name,p.pid)  # 打印子进程的名字,查看子进程的pid
        # p.terminate()         # 强行终止子进程
        # time.sleep(1)
        p.join()
        print(p.is_alive())   # 查看进程是否还活着
        print('')
    示例

     6)进程之间内存隔离

    from multiprocessing import Process
    import time,os
    
    x=100
    def task():
        global x
        x=0
    
    if __name__ == '__main__':
        p=Process(target=task)
        p.start()
    
        # p.join()
        print('',x)
    View Code

     7)多进程实现socket并发操作

    import socket
    from multiprocessing import Process
    
    def talk(conn):
        while True:
            try:
                data = conn.recv(1024)
                if not data: break
                print('客户端数据:%s' % data)
                conn.send(data.upper())
            except ConnectionResetError:
                break
    
        conn.close()
    
    def server(ip,port):
        phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式协议指的就是tcp协议
        phone.bind((ip,port)) #端口范围0-65535
        phone.listen(5) #限制的是请求数,而非链接数
    
        print('服务的启动......')
        while True: # 连接循环
            conn,client_addr=phone.accept()
            print(client_addr)
    
            # 通信循环
            # talk(conn)
            p=Process(target=talk,args=(conn,))
            p.start()
    
        phone.close()
    
    if __name__ == '__main__':
        server('127.0.0.1',8081)
    服务端
    import socket
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8081))
    
    while True:
        msg=input('>>>: ').strip()
        if not msg:continue
        phone.send(msg.encode('utf-8'))
    
        data=phone.recv(1024)
        print(data.decode('utf-8'))
    
    phone.close()
    客户端

    这种方法不科学,设置过多的进程,会大量消耗资源。并且,收到的并发数量大于进程数量,服务端会崩溃掉。

    原文链接:http://www.cnblogs.com/linhaifeng/articles/7428874.html

    三、线程

    1)线程介绍

    1 什么线程
        线程就是一条流水线的工作过程,一个进程内至少有一个线程
        进程只是一个资源单位
        而进程内的线程才执行单位
    
    2 为什么要多线程(进程vs线程)
        1、同一进程下的多个线程共享该进程内的数据
        2、线程的创建开销要远远小于进程的
    
    3 如何用线程
        1from threading import Thread
        2、t=Thread(target=def,args=(xxx,))

    2)开启线程的2种方式

    第一种,函数方式

    from threading import Thread
    import time,random
    
    def piao(name):
        print('%s is piaoing' %name)
        time.sleep(random.randint(1,3))
        print('%s is piao end' % name)
    
    if __name__ == '__main__':
        t=Thread(target=piao,args=('user',))
        t.start()
        print('')
    View Code

    第二种,类方式

    from threading import Thread
    import time,random
    
    class Mythread(Thread):
        def run(self):
            print('%s is piaoing' %self.name)
            time.sleep(random.randint(1,3))
            print('%s is piao end' % self.name)
    
    if __name__ == '__main__':
        t=Mythread()
        t.start()
        print('')
    View Code

     3)线程之间数据共享

    from threading import Thread
    
    x=100
    def task():
        global x
        x=0
    
    if __name__ == '__main__':
        t=Thread(target=task)
        t.start()
        t.join()
        print('',x)
    View Code

     4)线程的其他属性方法,如定义线程名,线程的存活状态

    from threading import Thread
    
    def task(name):
        print('%s is ruuning' %name)
    
    if __name__ == '__main__':
        t=Thread(target=task,args=('egon',),name='线程1')
        t.start()
        print(t.is_alive())
    
        t.setName('线程')
        print(t.getName())
        # print('')
    View Code

    在线程里查看自己的线程名的方法

    from threading import Thread,current_thread
    
    def task():
        print('%s is ruuning' %current_thread().getName())
    
    if __name__ == '__main__':
        t=Thread(target=task)
        t.setName('线程')
        t.start()
        print('')
    View Code

    5)同一个进程下的线程的pid一样,以及执行的效率比进程快近100倍。耗时:3.0161726474761963

    from threading import Thread,current_thread
    import os,time
    
    def task(n):
        print('%s is ruuning,pid:%s' %(current_thread().getName(),os.getpid()))
        time.sleep(n)
    
    if __name__ == '__main__':
        t1=Thread(target=task,args=(1,))
        t2=Thread(target=task,args=(2,))
        t3=Thread(target=task,args=(3,))
    
        start=time.time()
        t1.start()
        t2.start()
        t3.start()
    
        t1.join()
        t2.join()
        t3.join()
        print('',os.getpid())
        print(time.time()-start)
    View Code

    原文链接http://www.cnblogs.com/linhaifeng/articles/7428877.html

  • 相关阅读:
    指针,数组,字符串的区别(高质量程序设计指南C++/C语言第7章)
    bitset初始化问题
    书籍
    编译器的工作过程
    C++函数传递指向指针的指针的应用
    程序员面试金典--二叉树的下一个结点
    程序员面试金典--对称的二叉树
    程序员面试金典--按之字形顺序打印二叉树
    程序员面试金典--阶乘尾零
    程序员面试金典--矩阵元素查找
  • 原文地址:https://www.cnblogs.com/linu/p/9163873.html
Copyright © 2020-2023  润新知