day32 python 多线程 初始锁

day32 python 多线程 初始锁
 
一.操作系统/应用程序
                               用户
    应用程序:                  我们
                       框架开发人员
    应用程序: 解释器         程序员
    操作系统:                程序员
    硬件
 
二.操作中的并发,并行,线程,进程
    1.并发和并行
        并发:程序的并发执行, 多个程序在同一时间间隔运行,看到了一个同时运行的假象,实际同一时刻同一个cpu只能有一个程序被执行
        并行:这个才是同时执行
    2.单进程,单线程的应用程序
        之前写的程序(除了socketserver): 都是单进程,单线程的应用程序
        print('666')
    3.到底什么是线程,什么是进程.
        是操作系统的进程和线程  
    4.单进程,多线程的应用程序
import threading
def func(arg):
     print(arg)
t = threading.Thread(target=func,args=('666',))    #在这里创建了一个子线程,默认存在一个主线程
t.start()
    5.程序可创建多个进程,默认一个
      进程可创建多个线程,默认一个
    6.线程:工作的最小单元, 共享进程中所有的资源, 创建线程的原因: 每个线程可以分担一些任务,最终完成最后的结果
      进程:独立开辟内存空间,创建进程的原因: 为了进程之间的数据隔离(可以利用特殊的方式进行通信)
 
三.其他语言线程,进程
    java, c# 崇尚一个进程: 里面多个线程. 很少使用多进程
    python:多CPU时: 
        一个进程,同一时刻只有一个线程能被cpu执行.
        创建多个进程,实现和java, c#一样的效果,但是进程是浪费资源的
 
四.python中线程和进程(GIL锁: 全局解释器锁)    (interpreter 解释器)
    GIL锁: 多cpu时,限制一个进程,同一时刻,只有一个线程可以被cpu调度
    python唯一缺点: 多线程时,无法利用多 cpu 的优势
 
五.以上部分总结:
    1.操作系统帮开发者操作硬件
    2.程序员写好代码在操作系统上运行(依赖解释器)
    3.任务特别多时:
        你写好代码, 
        交给解释器运行,
        解释器读取你的代码,
        交给操作系统去执行,
        根据你的代码去选择创建多少线程/多少进程去执行
        3.1.之前写的程序是串行:
            这里的代码: 创建了单进程,里面有个单线程去执行任务
            操作系统调用硬件
import requests
import uuid
url_list = ['https://club2.autoimg.cn/album/g2/M00/E4/50/userphotos/2019/08/13/12/500_ChsEml1SPiqAWFwWAAiDwSxrzcg822.jpg',
            'https://club2.autoimg.cn/album/g2/M0B/E4/54/userphotos/2019/08/13/12/500_ChsEml1SPkqAC3U-AAgJb16oPbA580.jpg',
            'https://club2.autoimg.cn/album/g2/M05/E4/53/userphotos/2019/08/13/12/500_ChsEml1SPkCAGaEcAAhkEHqfWkw467.jpg',
            'https://club2.autoimg.cn/album/g25/M01/09/54/userphotos/2019/08/13/12/500_ChsEel1SPk6AXODKAAd2fOmE6kU698.jpg'
            ]
def task(url):
    rst = requests.get(url)
    file_name = str(uuid.uuid4()) + '.jpg'          #搞个不一样的文件名
    with open(file_name, mode='wb') as f:           #把下载的内容写入文件
        f.write(rst.content)
    print(rst.content)
for url in url_list:                                #有个问题, 线程不能任意开, 得有限制
   task(url)
        3.2.现在写的程序:(多线程的并发(由于GIL锁, 多线程对cpu利用的限制,这种并发程度要取决于cpu和io处理的占比))
            这里的代码: 创建了单进程,里面有 5 个线程去执行任务(1个主线程, 4个子线程)
            GIL锁,同一时间,同一进程里面只有一个线程能被cpu执行
                问题来了: (那这里的5个线程岂不是不能并发:不是的,因为socket连接不太占cpu资源,而是属于io操作.所以这里虽然有GIL锁,多线程也可以并发执行)
                    原因在于cpu的分时处理, 不是一个线程按着一直到执行完: cpu虽然不能并发, 但是当多个线程的cpu都执行完, io操作时就不用cpu,也就不被GIL限制了
                并发的程度:
                    取决于cpu和io操作的占比, cpu越少这种多线程并发越好, cpu占的越多这种多线程就相当于串行
            操作系统调用硬件
import threading
import requests
import uuid
url_list = ['https://club2.autoimg.cn/album/g2/M00/E4/50/userphotos/2019/08/13/12/500_ChsEml1SPiqAWFwWAAiDwSxrzcg822.jpg',
            'https://club2.autoimg.cn/album/g2/M0B/E4/54/userphotos/2019/08/13/12/500_ChsEml1SPkqAC3U-AAgJb16oPbA580.jpg',
            'https://club2.autoimg.cn/album/g2/M05/E4/53/userphotos/2019/08/13/12/500_ChsEml1SPkCAGaEcAAhkEHqfWkw467.jpg',
            'https://club2.autoimg.cn/album/g25/M01/09/54/userphotos/2019/08/13/12/500_ChsEel1SPk6AXODKAAd2fOmE6kU698.jpg'
            ]
def task(url):
    rst = requests.get(url)
    file_name = str(uuid.uuid4()) + '.jpg'              #搞个不一样的文件名
    with open(file_name, mode='wb') as f:               #把下载的内容写入文件
        f.write(rst.content)
    print(rst.content)
for url in url_list:                                    #有个问题, 线程不能任意开, 得有限制
    t = threading.Thread(target=task, args=(url,))
    t.start()
        4.python 多线程的并发程度,效率问题(GIL锁)
            计算密集型: 占用cpu的时间长, 相当于串行, 无并发效率可言 
            io密集型: 占用cpu的时间不长,并发效率高
         java,c# 多线程的并发, 没有GIL锁
            计算密集型:高
            io密集型:高
        5.python 多进程的并发程度(浪费资源,为了让计算密集型效率高,不得已而为之)
            计算密集型: 一个进程里搞一个线程,然后搞多个进程,可以效率高, 但是浪费资源 
            io密集型: 占用cpu的时间不长,并发效率高
         java,c# 多进程的并发(浪费资源,多线程可实现,基本不开多进程)
            计算密集型:高
            io密集型:高
        6.为什么GIL锁有短板, 还要有这个东西
            猜测:写的不够好, 当时开发的时候为了更快的开发出来没时间考虑
        7.以后写python时
            io操作型:用多线程    --    文件/输入输出/socket网络通信
            计算密集型:用多进程
 
五.python线程编写+锁
    1.多线程时,线程切换的时机: cpu执行100个指令    (interval 间隔)
import sys
print(sys.getcheckinterval())                             #100  python里面: cpu执行一个线程的100个cpu指令,就会切换另外一个线程
    2.简单线程的编写
import threading
def func(arg):
    print(arg)
t = threading.Thread(target=func,args=('666',))
t.start()
    3.主线程默认: 等子线程执行完, 然后一起退出
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=(5,))
t2.start()
 
print('123')                                                #主线程打印完123, 没有终止, 而是等子线程运行完,一起退出
    4.主线程: 不等我这个子线程(是否执行完), 主线程就先退出
              如果子没完,那么dead 
              t1.setDeamon(True), 
import threading
import time
def func(arg):
    time.sleep(arg)
    print(arg)
t1 = threading.Thread(target=func,args=(3,))
t1.setDaemon(True)
t1.start()
t2 = threading.Thread(target=func,args=(5,))
t2.setDaemon(True)                                          #子线程若想执行完,就要跟上主线程的脚步,否则dead
t2.start()
 
print('123')
    5.让开发者可以控制: 主线程站住别动,等我多少时间
        t.join():   无参数: 夯住, 一直等: 等我这个线程执行完, 再继续往下执行: 相当于不写多线程,不去并发
        t2.join(2): 有参数: 最多夯住2秒钟, 如果我这个线程1秒执行完,那么就无须等2秒
import threading
import time
def func(arg):
    time.sleep(arg)
    print(arg)
print('创建子线程t1')
t1 = threading.Thread(target=func,args=(3,))
t1.start()
t1.join()                                                   
print('创建子线程t2')
t2 = threading.Thread(target=func,args=(5,))
t2.start()
t2.join(2)                                                   
 
print('123')
    6.线程的名字 
        t1.setName(): 设置线程的名字 
        threading.current_thread().getName(): 获取当前线程的名字
import threading
import time
 
def func(arg):
    time.sleep(arg)                   
    t = threading.current_thread()                              #2.再获取当前线程的对象
    print(t.getName())                                          #3.然后从线程对象中取到名字
 
print('创建子线程t1')
t1 = threading.Thread(target=func,args=(3,))
t1.setName('线程1')                                             #1.1.先设置线程的名字
t1.start()
 
print('创建子线程t2')
t2 = threading.Thread(target=func,args=(5,))
t2.setName('线程2')                                             #1.2.先设置线程的名字
t2.start()
 
print('123')
    7.start()的解读
        start()只是说告诉cpu我准备好了(就绪态), 什么时候执行cpu说了算
import threading
 
def func(arg):
    print(arg)
 
t1 = threading.Thread(target=func,args=(666,))
t1.start()                      
 
print('123')
    8.面向对象版本的多线程
        8.1.方式一: 自定义一个类(继承threading.Thread),里面pass(这种比较常用)
import threading
 
class MyThread(threading.Thread):
    pass
 
def func(arg):
    print(arg)
 
t = MyThread(target=func, args=(666,))
t.start()
        8.2.方式二: 自定义类中不加函数这个参数,默认执行类中的run()方法
import threading
 
class MyThread(threading.Thread):
    def run(self):
        print(123,self._args, self._kwargs)
 
t = MyThread(args=(123,))                                          #如果参数里不加函数func, 那么默认会执行threading.Thread类中的run()方法
t.start()
 
    9.多线程的应用场景
        9.1.对计算密集型的这种无用
import threading
v1 = [11,22,33]
v2 = [44,55,66]
def func(data,plus):
    print([ i + plus for i in data])
t1 = threading.Thread(target=func, args=(v1,1))
t1.start()
t2 = threading.Thread(target=func, args=(v2,100))                  #GIL锁住了 cpu , 这种是没办法并发, 没法提高效率的
t2.start()
        9.2.有用的:比如之前的socket操作,是io密集型操作)
 
    10.初识锁: 排队执行
import threading
import time
 
lock = threading.RLock()                                        #创建锁
n = 10
def task(arg):
    print('这段代码不加锁')
    lock.acquire()                                               #加锁(acquire 获得)
    print('这段代码加锁')                                        #这段代码加锁,执行临界资源,其他子进程需等待
    global n
    time.sleep(1)
    print(arg, n)
    n = arg                
    lock.release()                                               #释放锁(release 释放)
for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    t.start()
time.sleep(2)
print(n)
 
 
 
总结:
    1.为什么要创建多线程?
        java,c#:由于线程是cpu工作的最小单元,创建多线程可以利用多核优势实现并行操作
        python: io密集型操作有并行优势,但不是多核优势实现的.
    2.为什么要创建进程?
        进程和进程之间做数据隔离
    3.python 多线程的特殊性(GIL锁)
        把进程中的多线程锁住了,所以不能利用多核优势
        不得已:开多进程,必然浪费资源
    4.线程的创建方式:
        Thread  
        MyThread
    5.线程其他
        join
        setDeamon
        setName
        threading.current_thread().getName()
    6.锁
        threading.Rlock().acquire()
        threading.Rlock().release()
 
 
 
 
 

猜你喜欢

转载自www.cnblogs.com/aiaii/p/12220886.html