day32

一、操作系统/应用程序

a. 硬件
  - 硬盘
  - CPU
  - 主板
  - 显卡
  - 内存条
  - 电源
  ...
b. 装系统(软件)
  - 系统就是一个由程序员写出来的软件, 该软件用于控制计算机的硬件, 让他们之间相互配合.
c. 装软件(安装应用程序)
  - QQ
  - 百度云
  - pycharm
  ...

二、并发和并行

并发, 伪 由于执行速度特别快, 人感觉不到停顿
并行, 真 创建10个人同时操作.

并行:指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。
并发:并发是指资源有限的情况下,两者交替轮流使用资源,比如一个单核cpu资源同时满足一个人使用,A用一会后,让给B,B用完继续给A,交替使用,目的是提高效率;

并行是从微观上,也就是在一个精确的时间片刻,有不同的程序在执行,这就要求必须有多个处理器。
并发是从宏观上,在一个时间段上可以看成是同时执行的。

三、线程/进程

a. 单进程、 单线程的应用程序
print('666')

b. 到底什么是线程?什么是进程?
Python自己没有这玩意,Python中调用的操作系统的线程和进程

c. 单进程、多线程的应用程序

    import threading
    print('666')

    def func(arg):
        print(arg)

    t = threading.Thread(target=func)
    t.start()

一个应用程序(软件),可以有多个进程(默认只有一个),一个进程中可以创建多个线程(默认一个)。

d. 故事: Alex甄嬛西游传

总结:
  1. 操作系统帮助开发者操作硬件。
  2. 程序员写好代码在操作系统上运行(依赖解释器)。
  3.当任务特别多,我们可以把以前的写法改进一下:

    import threading
    import requests
    import uuid

    url_list = [
'https://www3.autoimg.cn/newsdfs/g28/M05/F9/98/120x90_0_autohomecar__ChsEnluQmUmARAhAAAFES6mpmTM281.jpg', 'https://www2.autoimg.cn/newsdfs/g28/M09/FC/06/120x90_0_autohomecar__ChcCR1uQlD6AT4P3AAGRMJX7834274.jpg',
'https://www2.autoimg.cn/newsdfs/g3/M00/C6/A9/120x90_0_autohomecar__ChsEkVuPsdqAQz3zAAEYvWuAspI061.jpg',
    ]

    def task(url):
        ret = requests.get(url)
        file_name = str(uuid.uuid4()) + '.jpg'
        with open(file_name, mode='wb') as f:
            f.write(ret.content)

    for url in url_list:
        task(url)

    """
    - 你写好代码
    - 交给解释器运行: python s1.py 
    - 解释器读取代码,再交给操作系统去执行,根据你的代码去选择创建多少个线程/进程去执行(单进程/单线程)。
    - 操作系统调用硬件:硬盘、cpu、网卡....
    """
以前的我们写代码
    import threading
    import requests
    import uuid
    
    url_list = [
'https://www3.autoimg.cn/newsdfs/g28/M05/F9/98/120x90_0_autohomecar__ChsEnluQmUmARAhAAAFES6mpmTM281.jpg',
'https://www2.autoimg.cn/newsdfs/g28/M09/FC/06/120x90_0_autohomecar__ChcCR1uQlD6AT4P3AAGRMJX7834274.jpg',
'https://www2.autoimg.cn/newsdfs/g3/M00/C6/A9/120x90_0_autohomecar__ChsEkVuPsdqAQz3zAAEYvWuAspI061.jpg',
    ]
    
    
    def task(url):
        ret = requests.get(url)
        file_name = str(uuid.uuid4()) + '.jpg'
        with open(file_name, mode='wb') as f:
            f.write(ret.content)
    
    
    for url in url_list:
        t = threading.Thread(target=task, args=(url,))
        t.start()
    
    """
    - 你写好代码
    - 交给解释器运行: python s2.py 
    - 解释器读取代码,再交给操作系统去执行,根据你的代码去选择创建多少个线程/进程去执行(单进程/4线程)。
    - 操作系统调用硬件:硬盘、cpu、网卡....
    """
现在的我们写代码

 四、Python中线程和进程(GIL锁)

GIL锁(全局解释器锁): 用于限制一个进程中同一时刻只有一个线程被cpu调度。
扩展:默认GIL锁再执行100个cpu指令(过期时间)切换线程。

    import sys
    
    v1 = sys.getcheckinterval()
    print(v1)  # 100
查看方法

  问题1:为什么有这把GIL锁?
    python语言的创始人在开发这门语言时,目的是快速把语言开发出来,如果加上GIL锁(c语言加锁),切换时按照100条字节指令来进行线程间的切换。

  问题2:进程和线程的区别?
    线程,线程是cpu工作的最小单元;
    进程,进程是cpu资源分配的最小单元,为线程提供一个资源共享的空间;
    一个进程中可以有多个线程,一个进程中默认有一个主线程;
    对于python来说,它的进程和线程和其他语言有差异,有GIL锁。它保证一个进程中同一时刻只有一个线程被cpu调度;
    IO密集型操作可以使用多线程,计算密集型可以使用多进程;

  问题3:线程创建的越多越好吗?
    不是,线程之间进行切换,要做上下文管理。

五、线程的使用

1. 线程的基本使用

  import threading
  def func(arg):
      print(arg)

  t = threading.Thread(target=func,args=(11,)) # 创建一个子线程
  t.start()

  print(123)
  # 11
  # 123
  # 程序结束

2.主线程默认等子线程执行完毕,才结束程序

    import threading
    import time
    def func(arg):
        time.sleep(arg)
        print(arg)

    t1 = threading.Thread(target=func,args=(3,))
    t1.start()

    t2 = threading.Thread(target=func,args=(9,))
    t2.start()

    print(123)
    
    # 3  # 程序开始3秒后
    # 6  # 程序开始6秒后
    # 程序结束

3、主线程不再等,当主线程终止则所有子线程也终止,使用setDaemon(True)

    import threading
    import time
    def func(arg):
        time.sleep(2)
        print(arg)
    
    t1 = threading.Thread(target=func,args=(3,))
    t1.setDaemon(False)  # 主线程等待此子进程(默认是False等待)
    t1.start()
    
    t2 = threading.Thread(target=func,args=(9,))
    t2.setDaemon(True)  # 主线程等待此子进程(需要设置)
    t2.start()
    
    print(123)

4、开发者可以控制主线程等待子线程(最多等待时间),使用join(n)

    import threading
    import time
    def func(arg):
        time.sleep(arg)
        print(arg)
    
    print("创建子线程t1")
    t1 = threading.Thread(target=func,args=(3,))
    t1.start()
    # 无参数: 让主线程在这里等着,直到子线程t1执行完毕,才可以往下走.
    # 有参数: 让主线程在这里最多等n秒,无论是否执行完毕,会继续往下走.
    t1.join(2)
    
    print("创建子线程t2")
    t2 = threading.Thread(target=func,args=(1,))
    t2.start()
    t2.join(3)  # 让主线程在这里等着,最多等待3秒,会继续往下走

    print(123)
    # 创建子线程t1
    # 创建子线程t2
    # 3
    # 1
    # 123
    # 程序结束

5、线程名称

    import threading
    def func(arg):
        t = threading.current_thread()  # 获取当前执行该函数的线程的对象
        name = t.getName()  # 根据当前线程对象获取当前线程名称
        print(name, arg)
    
    t1 = threading.Thread(target=func,args=(11,))
    t1.setName('李白')
    t1.start()
    
    t2 = threading.Thread(target=func,args=(22,))
    t2.setName('白居易')
    t2.start()
    
    print(123)
    # 李白 11
    # 白居易 22
    # 123

6、线程本质

   # 先打印:11?123?
    import threading
    def func(arg):
        print(arg)

    t1 = threading.Thread(target=func,args=(11,))
    t1.start()
    # start 是开始运行线程吗?不是
    # start 告诉cpu,我已经准备就绪,你可以调度我了。
    print(123)

7、补充: 面向对象版本的多线程

    import threading
    def func(arg):
        print(arg)

    t1 = threading.Thread(target=func,args=(11,))
    t1.start()
常见的创建多线程的方式
    import threading
    class MyThread(threading.Thread):
        def run(self):
            print(11111,self._args,self._kwargs)

    t1 = MyThread(args=(11,))
    t1.start()  # 在cpu内部,如果要调度这个线程的话会执行这个对象的run方法

    t2 = MyThread(args=(22,))
    t2.start()

    print('end')
    # 11111 (11,) {}
    # 11111 (22,) {}
    # end
面对对象方式(一般不用,了解即可))

六、Python中线程编写

1. 计算密集型多线程无用

    impot threading
    v1 = [11, 22, 33]  # +1
    v2 = [44, 55, 66]  # +100
    
    def func(data, plus):
        for i in range(len(data)):
            data[i] = data[i] + plus
    
    t1 = threading.Thread(target=func,args=(v1,1))
    t1.start()
    
    t2 = threading.Thread(target=func,args=(v2,100))
    t2.start()

2. IO操作,多线程有用

    import threading
    import requests
    import uuid
    
    url_list = [ 
  'https://www3.autoimg.cn/newsdfs/g28/M05/F9/98/120x90_0_autohomecar__ChsEnluQmUmARAhAAAFES6mpmTM281.jpg',
  'https://www2.autoimg.cn/newsdfs/g28/M09/FC/06/120x90_0_autohomecar__ChcCR1uQlD6AT4P3AAGRMJX7834274.jpg',
  'https://www2.autoimg.cn/newsdfs/g3/M00/C6/A9/120x90_0_autohomecar__ChsEkVuPsdqAQz3zAAEYvWuAspI061.jpg', ] def task(url): ret = requests.get(url) file_name = str(uuid.uuid4()) + '.jpg' with open(file_name, mode='wb') as f: f.write(ret.content) for url in url_list: t = threading.Thread(target=task, args=(url,)) t.start()

3. 多线程的问题(加锁和释放锁)

    import time
    import threading
    
    lock = threading.RLock()
    
    n = 10
    
    def task(i):
        print('这段代码不加锁',i)
    
        lock.acquire()  # 加锁,此区域代码同一时刻只能有一个线程执行
        global n
        print('当前线程',i,'读取到的n的值为: ',n)
        n = i
        time.sleep(1)
        print('当前线程',i,'修改的n的值为: ',n)
        lock.release()  # 释放锁
    
    for i in range(10):
        t = threading.Thread(target=task, args=(i,))
        t.start()

总结:
  1. 应用程序/进程/线程的关系? *****(面试题: 进程/线程/协程的区别)
    工厂,厂房,工人, 工人共享厂房里的所有资源
    注意:线程是为了工作

  2. 为什么要创建线程?
    由于线程是cpu工作的最小单元,创建线程可以利用多核优势实现并行操作(Java/C#).

  3. 为什么要创建进程?
    进程和进程之间做数据隔离(Java/C#)
    注意:进程是为了提高环境让线程工作

  4. Python
    a. Python中存在一个GIL锁. *****
      - 造成: 多线程无法利用多核优势
      - 解决: 开多进程处理(浪费资源)
      进程和线程如何选择使用?
        IO密集型:多线程
        计算密集型:多进程
    b. 线程的创建
      - Thread    *****
      - MyTread
    c. 其他
      - join          *****
      - setDeanon   *****
      - setName      *****
      - threading.current_thread()   *****
    d. 锁
      - 加锁
      - 释放

原文地址:https://www.cnblogs.com/kangqi452/p/11762923.html