zoukankan      html  css  js  c++  java
  • 107、进程与线程

    进程、线程、与协成

    1.1 进程介绍

    程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程

    程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。

    在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

    进程的缺点:

    • 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

    • 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

    1.2 线程介绍

    线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务

    1.3 线程和进程的区别

    • 进程的内存独立,线程共享同一进程的内存
    • 进程是资源的集合,线程是执行单位
    • 进程之间不能之间互相访问,线程可以相互通信
    • 创建新进程非常消耗资源,线程非常轻量,只保存线程需要运行时的必要数据,如上下文
    • 同一进程里的线程可以相互控制,父线程可以控制子线程
    • io 密集型应用使用多线程,
    • 计算密集型应用使用多进程

    1.4 GIL 锁

    python 中某一进程中的多个线程只能被一个 CPU 调用,因为在线程中存在 GIL(全局解释器锁)。而C语言一个进程的多线程可以分别被不同的 CPU 调用

    2、threading模块

    2.1 线程的继承

    import threading
    import time
    
    class MyThread(threading.Thread):
        def __init__(self,num):
            threading.Thread.__init__(self)
            self.num = num
        def run(self):#定义每个线程要运行的函数
            print("running on number:%s" %self.num)
            time.sleep(3)
    
    if __name__ == '__main__':
        t1 = MyThread(1)
        t2 = MyThread(2)
        t1.start()
        t2.start()
    

    2.2 创建多线程与join

    import threading,time
    
    def hellow(msg):
        print('this is hellow %s' %msg)
        time.sleep(1)
    
    if __name__ == '__main__':
        thread_list = []
    
        for i in range(10):
            t1 = threading.Thread(target=hellow,args=(i,))#生成一个线程
            t1.start()
            thread_list.append(t1)
            print(t1.getName())#获取线程名
        for r in thread_list:
            r.join()#等待该线程执行完毕再往下执行,join(2)就是等待的最长时间
        print('-------主线程-------')
    

    2.3 守护线程Daemon

    import threading,time
    '''守护线程会在主线程执行完毕时立刻断开'''
    def sayhi(msg):
    
        time.sleep(0.001)
        print('this is msg',msg)
    
    if __name__ == '__main__':
        for i in range(10):
            t1 = threading.Thread(target=sayhi,args=(i,))
            t1.setDaemon(True)#将 t1 设为主线程的守护线程
            t1.start()
        print('------主线程------')
    

    每次输出的结果会有不同,有的线程没执行完就跟着主线程down了

    2.4 小结

    • start 线程准备就绪,等待CPU调度
    • setName 为线程设置名称
    • getName 获取线程名称
    • setDaemon 设置为后台线程或前台线程(默认),如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止;如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
    • join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
    • run 线程被cpu调度后自动执行线程对象的run方法

    2.5 线程锁

    单线程锁 Lock、RLock
    import threading,time
    
    num = 10
    #lock = threading.Lock()#创建线程锁,一次只能锁一个,只能一次申请锁
    lock = threading.RLock()#创建递归锁,一次只能锁一个,多次次申请锁
    #lock = threading.BoundedSemaphore(3)#创建信号量锁,一次锁定3人
    def task(arg):
        lock.acquire()#申请使用锁
        lock.acquire()#RLock多次申请使用锁,递归锁
        time.sleep(1)
        global num
        num -= 1
        print(num)
        lock.release()#释放锁
        lock.release()#RLock多次释放锁
    
    for i in range(10):
        t = threading.Thread(target=task,args=[i,])
        t.start()
    
    print('-----主线程-----',num)
    
    多线程 Semaphore
    lock = threading.BoundedSemaphore(3)#三线程锁
    
    def task(arg):
        # 申请使用锁,其他人等
        lock.acquire()
        time.sleep(1)
        global v
        v -= 1
        print(v)
        # 释放
        lock.release()
        
    for i in range(10):
        t = threading.Thread(target=task,args=(i,))
        t.start()
    
    事件 event

    python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
    事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞

    • clear:将“Flag”设置为False
    • set:将“Flag”设置为True
    lock = threading.Event()
    
    def task(arg):
        time.sleep(1)
        # 锁住所有的线程
        lock.wait()
        # 申请使用锁,其他人等
        print(arg)
    
    for i in range(10):
        t = threading.Thread(target=task,args=(i,))
        t.start()
    while True:
        value = input('>>>>')
        if value == '1':
            lock.set()
            lock.clear()
    
    条件 Condition

    使得线程等待,只有满足某条件时,才释放n个线程

    lock = threading.Condition()
    
    def task(arg):
        time.sleep(1)
        # 锁住所有的线程
        lock.acquire()
        lock.wait()
        # 申请使用锁,其他人等
        print('线程',arg)
        lock.release()
        
    for i in range(10):
        t = threading.Thread(target=task,args=(i,))
        t.start()
    while True:
        value = input('>>>>')
        lock.acquire()
        lock.notify(int(value))
        lock.release()
    
    定时器 Timer

    定时器,指定n秒后执行某操作

    from threading import Timer
    
    def hello():
        print("hello, world")
    
    t = Timer(1, hello)
    t.start()  # after 1 seconds, "hello, world" will be printed
    

    2.5 线程池

    from concurrent.futures import ThreadPoolExecutor
    import time,requests
    
    pool = ThreadPoolExecutor(2)#定义线程池每次最多运行两个线程
    url_list = [
        'http://www.baidu.com',
        'http://www.oldboyedu.com',
        'http://www.qq.com',
     ]
    
    def download(url):
        response = requests.get(url)
        #print(response)
        return response
    
    def option(future):
        download_response = future.result()
        print('option',download_response.url,download_response.status_code,download_response.text)
    
    for url in url_list:
        print('开始请求',url)
        future = pool.submit(download,url)#去线程池获取连接
        future.add_done_callback(option)#调用option函数,并将download的结果传参
    

    3、multiprocessing 模块

    进程各自持有一份数据,默认无法共享数据

    from multiprocessing import Process
    import time
    
    def option(num,li):
        li.append = num
        print(li)
    
    if __name__ == '__main__':
        v = []
        # for i in range(5):
        #     p = threading.Thread(target=option,args=(i,v,))
        #     p.start()
        for i in range(5):
            p1 = Process(target=option,args=(i,v,))
            p1.start()
    

    3.1 进程间数据共享

    方法一:Array

    from multiprocessing import Process,Array,Manager
    import threading
    
    def option(num,li):
        li[num] = 1
        print(list(li))
    
    if __name__ == '__main__':
        v = Array('i',5)#('数据类型',长度)
        for i in range(5):
            p = Process(target=option,args=(i,v,))
            p.start()
    

    方法二:manage.dict()共享数据

    from multiprocessing import Process,Array,Manager
    import threading
    
    def option(num,li):
        li.append(num)
        print('打印信息',li)
    
    if __name__ == '__main__':
        v = Manager().list()
        for i in range(10):
            p = Process(target=option,args=(i,v,))
            p.start()
            p.join()
    

    3.2 进程池

    进程锁与线程锁一样
    进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。

    from concurrent.futures import ProcessPoolExecutor
    from multiprocessing import Process,Manager
    
    def call(arg):
        data = arg.result()
        print(data)
    
    def option(arg):
        print('打印信息',arg)
        return arg + 100
    if __name__ == '__main__':
        pool = ProcessPoolExecutor(5)
        for i in range(10):
            obj = pool.submit(option,i)
            obj.add_done_callback(call)
    

    4、协成

    协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

    协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

    from greenlet import greenlet
    
    def test1():
        print(12)
        gr2.switch()
        print(34)
        gr2.switch()
    
    def test2():
        print(56)
        gr1.switch()
        print(78)
    
    gr1 = greenlet(test1)
    gr2 = greenlet(test2)
    gr1.switch()
    

    根据协程二次开发:协程+IO

    from gevent import monkey; monkey.patch_all()
    import gevent
    import requests
    
    def f(url):
        response = requests.get(url)
        print(response.url,response.status_code)
    
    gevent.joinall([
            gevent.spawn(f, 'http://www.oldboyedu.com/'),
            gevent.spawn(f, 'http://www.baidu.com/'),
            gevent.spawn(f, 'http://github.com/'),
    ])
    
  • 相关阅读:
    Struts类型转换
    Oracle的学习,知识点整理
    常用的Oracle_SQL语句
    Oracle的优化
    Oracle创建表空间,用户,授权
    Linux权限的控制
    Oracle实例的3种连接方式和所使用的连接工具
    Oracle数据库打开端口
    Hibernate一对多配置
    hibernate UUID问题
  • 原文地址:https://www.cnblogs.com/workhorse/p/6565296.html
Copyright © 2011-2022 走看看