zoukankan      html  css  js  c++  java
  • 9-3 线程,线程池,守护线程,锁,协程

    一 线程

    # 什么是进程 :是计算机资源分配的最小单位
    # 什么是线程
    # 线程和进程的关系 :
    # 每一个进程中都至少有一个线程
    #开启100个线程
     1 import os
     2 import time
     3 from threading import Thread
     4 n=100
     5 def func(i):
     6     global n
     7     time.sleep(1)
     8     n-=1
     9     print(os.getpid(),'thread%s'%i)
    10 l=[]
    11 for i in range(100):
    12     t=Thread(target=func,args=(i,))
    13     t.start()
    14     l.append(t)
    15 for i in l:
    16     i.join()
    17 print(n)


    n的结果是0

    总结:

     #每个进程里至少有一个主线程负责执行代码
    # 在主线程中可以再开启一个新的线程
    # 在同一个进程中就有两个线程同时在工作了
    # 线程才是CPU调度的最小单位
    # 多个线程之间的数据时共享的

    二 守护线程
    # 主线程结束了之后守护线程也同时结束
    # 守护线程会等待主线程完全结束之后才结束
    from threading import Thread
    import time
    def foo():
        while True:
            print(123)
            time.sleep(1)
    def bar():
        print(456)
        time.sleep(3)
        print('end456')
    t1=Thread(target=foo)
    t2=Thread(target=bar)
    t1.daemon=True #foo是守护线程
    t1.start()
    t2.start()
    print("main----") #主线程结束的地方
    
    打印结果:
    123
    456
    main----
    123
    123
    end456

     三 锁(Lock)

    1普通锁 lock

    # 当你的程序中出现了取值计算再赋值的操作 数据不安全 —— 加锁
     1 from threading import Thread,Lock
     2 import time
     3 def work():
     4     global n
     5 
     6     lock.acquire()
     7     temp=n
     8     time.sleep(0.1)
     9     n=temp-1
    10     lock.release()
    11 if __name__ == '__main__':
    12     lock = Lock()
    13     n=100
    14     l=[]
    15     for i in range(10):
    16         p=Thread(target=work)
    17         l.append(p)
    18         p.start()
    19     for i in l:
    20         i.join()
    21     print(n)
    22 
    23 最后n的值为90

    2 递归锁(Rlock)(很少用)

    1 from threading import RLock
    2 
    3 lock = RLock()
    4 lock.acquire()
    5 lock.acquire()
    6 print(123)
    7 lock.release()
    8 print(456)
    9 lock.release()

    总结:

    # 普通的锁 在同一个线程中 只能acquire一次
    # 所以当acquire两次的时候就容易出现死锁现象
    # 出现了死锁现象可以使用递归锁去解决问题
    # 但是本质上死锁的出现是因为逻辑的错误
    # 因此我们更应该把注意力集中在解决逻辑错误
    # 而不要在出现错误的时候直接用递归锁规避

    递归锁并没有本质上解决死锁的问题

    四 线程池(ThreadPoolExecutor)

    1

     1 import time
     2 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
     3 def func(num):
     4     print(num)
     5     time.sleep(1)
     6     print(num)
     7 if __name__ == '__main__':
     8     t=ThreadPoolExecutor(20) #20个线程,每次处理20个
     9     for i in range(50):
    10         t.submit(func,i) #异步提交命令
    11     t.shutdown()#同join整个线程池
    12     print('done')

    2 回调函数(callback)

     1 # import time
     2 # import random
     3 # from concurrent.futures import ThreadPoolExecutor
     4 # from threading import current_thread
     5 # urls=[
     6 #         'https://www.baidu.com',
     7 #         'https://www.python.org',
     8 #         'https://www.openstack.org',
     9 #         'https://help.github.com/',
    10 #         'http://www.sina.com.cn/'
    11 #         'http://www.cnblogs.com/'
    12 #         'http://www.sogou.com/'
    13 #         'http://www.sohu.com/'
    14 #     ]
    15 #
    16 # def analies(content):
    17 #     print('分析网页',current_thread())
    18 #     print(content.result())
    19 #
    20 #
    21 # def get_url(url):
    22 #     print('爬取网页',current_thread())
    23 #     time.sleep(random.uniform(1,3))
    24 #     # analies(url*10)
    25 #     return url*10
    26 #
    27 # t = ThreadPoolExecutor(3)
    28 # print('主线程',current_thread())
    29 # for url in urls:
    30 #     t.submit(get_url,url).add_done_callback(analies) #
    #回调函数当执行了get_url之后,得到了一个url*10 ,然后在立即执行analies函数,并传给content参数

    # concurrent.futures里面的 callback是由子线程做的
     五协程(gevent)
    需要手动安装,第三方模块
    安装 :pip3 install gevent
    协程 把一个线程拆分成几个

    # 协程 是程序级别调度
    # 减轻了操作系统的负担、增强了用户对程序的可控性
    
    
    特点是:只要遇到阻塞就会执行别的任务。例如:
    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
    def eat(name):
        print('%s eat 1' %name)
        time.sleep(2)
        print('%s eat 2' %name)
    
    def play(name):
        print('%s play 1' %name)
        time.sleep(1)
        print('%s play 2' %name)
    
    
    g1=gevent.spawn(eat,'egon')
    g2=gevent.spawn(play,'alex')
    g1.join()
    g2.join()
    # gevent.joinall([g1,g2])#第二种写法
    print('')
    执行结果是:

    egon eat 1
    alex play 1
    alex play 2
    egon eat 2



    例2:利用协程批量访问url
     1 from gevent import monkey;monkey.patch_all() #这个是必须加的否则gevent不识别time
     2 import gevent
     3 import requests
     4 import time
     5 
     6 def get_page(url):
     7     print('GET: %s' %url)
     8     response=requests.get(url)
     9     if response.status_code == 200:
    10         print('%d bytes received from %s' %(len(response.text),url))
    11 
    12 
    13 start_time=time.time()
    14 gevent.joinall([
    15     gevent.spawn(get_page,'https://www.python.org/'),
    16     gevent.spawn(get_page,'https://www.yahoo.com/'),
    17     gevent.spawn(get_page,'https://github.com/'),
    18 ])
    19 stop_time=time.time()
    20 print('run time is %s' %(stop_time-start_time))



  • 相关阅读:
    [公告]博客园准备建立SharePoint团队
    [公告]新增三款Skin
    又新增三款Skin
    一个不错的计数器
    [公告]新建新手区
    2005年1月16日 IT Pro 俱乐部活动纪实
    [好消息]祝成科技.微软公司.博客园联合打造IT俱乐部
    [公告]SharePoint团队正式成立
    SharePoint文档库存在问题
    [活动]2004年计算机图书评选
  • 原文地址:https://www.cnblogs.com/huningfei/p/9188479.html
Copyright © 2011-2022 走看看