• 并发编程 线程


    线程

    在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程

    线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程

    车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线

    流水线的工作需要电源,电源就相当于cpu

    所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。

    多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。

    例如,北京地铁与上海地铁是不同的进程,而北京地铁里的13号线是一个线程,北京地铁所有的线路共享北京地铁所有的资源,比如所有的乘客可以被所有线路拉。

    开启线程的两种方式

    复制代码
    from threading import Thread
    import time,os
    
    # 第一种方式
    def task():
        print('%s is running' %os.getpid())
        time.sleep(2)
        print('%s is done' %os.getpid())
    
    if __name__ == '__main__':
        t = Thread(target=task,)
        t.start()
        print('主')
        '''
        1.一个子进程内不开进程也不开子线程:主线程结束,该进程就结束
        2.当一个进程内开启子进程时:
            主线程结束,主进程要等,等所有子进程运行完毕给儿子收尸
        3.当一个进程内开启多个线程时:
            主线程结束并不意味着进程结束,
            进程的结束指的是该进程内所有的线程都运行完毕,才应该回收进程
        '''
    # 第二种方式
    class Mythread(Thread):
        def __init__(self):
            super().__init__()
        def run(self):
            print('%s is running' % os.getpid())
            time.sleep(2)
            print('%s is done' % os.getpid())
    
    if __name__ == '__main__':
        t = Mythread()
        t.start()
        print('主')
    复制代码

    多线程指的是,在一个进程中开启多个线程,简单的讲:如果多个任务共用一块地址空间,那么必须在一个进程内开启多个线程。详细的讲分为4点:

      1. 多线程共享一个进程的地址空间

          2. 线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用

          3. 若多个线程都是cpu密集型的,那么并不能获得性能上的增强,但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠运行,从而会加快程序执行的速度。

          4. 在多cpu系统中,为了最大限度的利用多核,可以开启多个线程,比开进程开销要小的多。(这一条并不适用于python)

    线程对象的其它属性和方法

    复制代码
    from threading import Thread,current_thread,enumerate,active_count
    import time,os
    
    def task():
        print('%s is running' %current_thread().getName())  # 线程名 current_thread()当前线程
        time.sleep(2)
        print('%s is done' %os.getpid())
    
    if __name__ == '__main__':
        # t = Thread(target=task,name='xxx')
        t = Thread(target=task)
        t.start()
        # t.join() 
        # print(t.name)  # 线程名 Thread-1
        print(enumerate())  # 当前或者的线程对象
        print(active_count())  # 当前活着线程的线程数
        print('主',current_thread().getName())  # MainThread
    复制代码

    线程与进程内存空间占用

    复制代码
    # 进程之间内存空间隔离
    from multiprocessing import Process
    
    n = 100
    def task():
        global n
        n = 0
    
    if __name__ == '__main__':
        t = Process(target=task,)
        t.start()
        t.join()
        print('主',n)  # 100
    
    # 线程之间内存空间共享
    from threading import Thread
    
    n = 100
    def task():
        global n
        n = 0
    
    if __name__ == '__main__':
        t = Thread(target=task,)
        t.start()
        t.join()
        print('主',n)  # 0
    复制代码

    线程池

    复制代码
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    from threading import current_thread
    import time,random,os
    def task(n):
        print('%s is running'%current_thread().getName())
        time.sleep(random.randint(1,3))
        return n**2
    
    if __name__ == '__main__':
        # t = ProcessPoolExecutor()  # 默认是cpu的核数
        # print(os.cpu_count())  # 查看cpu核数
        t = ThreadPoolExecutor(3)  # 默认为cpu的核数*5
        objs = []
        for i in range(10):
            obj = t.submit(task,i)
            objs.append(obj)
        t.shutdown(wait=True)
        for obj in objs:
            print(obj.result())
        print('主',current_thread().getName())
    复制代码

    异步调用和回调函数

    复制代码
    import requests
    from threading import Thread,current_thread
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    import time
    
    def get(url):
        print('%s GET %s'%(current_thread().getName(),url))
        response = requests.get(url)
        if response.status_code == 200:
            return {'url':url,'text':response.text}
        # print(type(response.text))  # <class 'str'>
    
    def parse(obj):
        res = obj.result()
        print('[%s] <%s> (%s)' % (current_thread().getName(), res['url'],len(res['text'])))
        # print('[%s] parse res [%s]'%(res['url'],len(res['text'])))
    
    if __name__ == '__main__':
        urls = [
            'https://www.python.org',
            'https://www.baidu.com',
            'https://www.jd.com'
        ]
        t = ThreadPoolExecutor(2)
        # t = ProcessPoolExecutor(2)
        for url in urls:
            t.submit(get,url).add_done_callback(parse)  # parse(obj)
        t.shutdown(wait=True)
        print('主')
    
    '''
    异步调用:
        提交完任务(为该任务绑定一个回调函数),不用在原地等任务执行完毕拿到结果,可以直接提交下一个任务
        一个任务一旦执行完毕就会自动触发回调函数的运行
    回调函数的参数是单一的:
        回调函数的参数就是它所绑定任务的返回值
    '''
    # 进程池,回调的活由主进程干
    # 线程池,回调的活都有可能干
    复制代码
  • 相关阅读:
    学习ReentrantLock
    新博客地址:WWW.BG7YWL.COM
    LimeSDR 无线信号重放攻击和逆向分析
    LimeSDR 上手指南
    GSM:嗅探语音流量
    制作一个老旧C118的GSM便携式测试设备
    SMS PDU编码数据串格式分析
    闪付卡(QuickPass)隐私泄露原理
    低成本制作基于OpenWRT的渗透工具
    Inside a low budget consumer hardware espionage implant
  • 原文地址:https://www.cnblogs.com/QQ279366/p/7954325.html
Copyright © 2020-2023  润新知