• threading多线程模块


    1 基本实现

    Thread(target=函数名,args=(以元组形式传递的实参,要加","))

    th = threading.Thread(target=run,args=(i,))就是实例化一个线程

    th.start()就是对实例调用启动函数。

    在内部,主线程就会创建30个子线程,子线程并发执行,所以虽然每个线程sleep(3),但是是并发执行的。

    进程总耗时3s出头。

    import time
    import threading
    def run(arg):
        print('run %s' %(arg))
        time.sleep(3)
    
    for i in range(30):
        th = threading.Thread(target=run,args=(i,))
        th.start()
    

     

    2 深入理解线程

    2.1 主线程和子线程

    程序执行时,产生一个进程,进程里首先有一个主线程,每个执行的是子线程

    使用threading.current_thread(),发现在for循环中是主线程,在run函数里是子线程。

    import time
    import threading
    def run(arg):
        print('run %s' %(arg))
        time.sleep(3)
    
    for i in range(3):
        th = threading.Thread(target=run,args=(i,))
        print(threading.current_thread())
        th.start()
    >>>>
    <_MainThread(MainThread, started 13112)>
    run 0
    <_MainThread(MainThread, started 13112)>
    run 1
    <_MainThread(MainThread, started 13112)>
    run 2
    

      

    import time
    import threading
    def run(arg):
        print('run %s' %(arg))
        print(threading.current_thread())
        time.sleep(3)
    
    for i in range(3):
        th = threading.Thread(target=run,args=(i,))
        th.start()
    >>>>
    run 0
    <Thread(Thread-1, started 9976)>
    run 1
    <Thread(Thread-2, started 9980)>
    run 2
    <Thread(Thread-3, started 10800)>
    

      

    2.2 子线程的并发性

    cost_time的打印值:cost time 0.07500743865966797

    发现没有在上面的for循环结束后再执行cost_time = time.time() - start_time语句。

    因为每个实例化线程就是创建子线程,创建完后子线程自己去运行。主线程马上进入下一个执行语句,不会等待子线程执行完毕。

    import time
    import threading
    def run(arg):
        print('run %s' %(arg))
        time.sleep(10)
    
    start_time = time.time()
    
    for i in range(600):
        th = threading.Thread(target=run,args=(i,))
        th.start()
    
    cost_time = time.time() - start_time
    print('cost time', (cost_time))

    3 JOIN()

    就是wait函数,一个线程调用join(),主线程就会等子线程执行完毕再往下走.

    在这个程序中,相当于线程单并发顺序执行。

    import time
    import threading
    def run(arg):
        print('run %s' %(arg))
        time.sleep(1)
    
    start_time = time.time()
    
    for i in range(10):
        th = threading.Thread(target=run,args=(i,))
        th.start()
        th.join()
    
    cost_time = time.time() - start_time
    print('cost time', (cost_time))
    

    >>>

    E:Python2018L1venvScriptspython.exe E:/Python/2018L1/test/threadingserver.py
    run 0
    run 1
    run 2
    run 3
    run 4
    run 5
    run 6
    run 7
    run 8
    run 9
    cost time 10.010000944137573
    

      

    创建空列表t_list,然后把线程实例作为元素追加到列表(不在这里写join,不然会在每个线程中顺序sleep。)

    在一个for循环里,让每个实例执行join()。

    最后cost time 3.002300262451172

    import time
    import threading
    def run(arg):
        print('run %s' %(arg))
        time.sleep(3)
    
    start_time = time.time()
    t_list = []
    for i in range(10):
        th = threading.Thread(target=run,args=(i,))
        th.start()
        t_list.append(th)
    
    for i in t_list:
        i.join()
    
    cost_time = time.time() - start_time
    print('cost time', (cost_time))
    

      

    4 setDaemon

    当主线程退出,守护子线程不管有没有执行完毕,都会马上退出。

    对于非守护子线程,主线程会等它执行完毕再退出。

    对于守护线程,主线程不会等它执行完毕,而是直接退出。

    4.1

    以下程序,虽然没有join函数,但主线程也会在所有线程执行完毕后才退出。

    import time
    import threading
    def run(arg):
        print('run %s' %(arg))
        time.sleep(3)
        print(threading.current_thread())
    
    for i in range(3):
        th = threading.Thread(target=run,args=(i,))
        #th.setDaemon(True)
        th.start()
    

     

    4.2

    如果把子线程都设置为守护线程,就是说主线程不会在它执行完毕再退出,而是直接退出。

    print(threading.current_thread())还没执行,就已经退出,不会再执行

    import time
    import threading
    def run(arg):
        print('run %s' %(arg))
        time.sleep(3)
        print(threading.current_thread())
    
    for i in range(3):
        th = threading.Thread(target=run,args=(i,))
        th.setDaemon(True)
        th.start()
    

      

    4.3

    主线程的执行时间比守护子线程长。在主线程退出前子线程已执行完毕。

    import time
    import threading
    def run(arg):
        print('run %s' %(arg))
        time.sleep(3)
        print(threading.current_thread())
    
    for i in range(3):
        th = threading.Thread(target=run,args=(i,))
        th.setDaemon(True)
        th.start()
    time.sleep(4)
    

      

    5 GIL全局解释器锁

    在cpython上,使用gil(global interpreter lock),来保证同一时间只有一个线程在解释器中运行。所有的线程并发都是通过context switch来完成。

    这使得python无法真正使用多核cpu。

    6 线程锁(互斥锁)

    6.1 线程共享数据池

    在进程中的多线程,共享进程的内存空间。就是可以对同一个数据进行操作

    import time
    import threading
    num = 0
    def run():
        global num  #函数使用全局变量,需要global声明
        time.sleep(2)
        num += 1
        print('thread:',num)
    for i in range(20):
        th = threading.Thread(target=run)
        th.start()
    time.sleep(5)
    print(num)
    

      

    6.2 线程锁的作用

    多个线程同时操作同一块数据,假设t1先拿到data = 0,然后使用解释器,调用cpu运算 data ++。还没运算完成,就被context switch。

    这时t2也操作data,这是data = 0,然后使用解释器,调用cpu运算data ++,然后把data = 1写入data数据。

    这时切换回t1,t1从context里拿到数据data = 0,运算完成,然后把data = 1写入data数据。

    这样就出错了。

    所以需要对data这块数据上锁,同一时间只能有一个线程进行操作。

    python3上程序会自动加锁。

    6.3 死锁和递归锁

    https://blog.csdn.net/u013210620/article/details/78723704

    用得着再看

    6.4 信号量

    同一时间有几个线程可以访问共享数据。

    信号量就是多把锁,允许同时多个线程运行。

    可以控制同一时刻可以运行的线程数。

    7 Event事件

    用于实现线程间通信

    set表示设定标志位,clear是清空标志位,在处理标志位的函数体中

    wait是等待标志位为set,is_set是判断标志位是否为set,在检测标志位的函数体中

    import time
    import threading
    
    event = threading.Event()
    
    #-----红绿灯函数,在红灯时标志位清零,绿灯设置标志位-----
    def lighter():
        count = 0
        event.set()
        while True:
            count += 1
            time.sleep(1)
            if count > 20 and count <= 30:
                event.clear()   #20-30s,红灯
                print('33[41mred light on33[0m')
            elif count > 30:
                event.set()
                count = 0
            else:
                print('33[42mgreen light on33[0m')
    
    #----汽车函数,红灯停绿灯行,每秒1km----
    def car(name):
        global mileage
        while True:
            if event.is_set():
                mileage += 1
                time.sleep(1)
                print('%s is running,the mileage is %s kilometer' % (name,mileage))
            else:
                time.sleep(1)
                print('%s is wait' %(name))
                event.wait()    #等待标志位设为set
    
    if __name__ == '__main__':
        mileage = 0
    
        car1 = threading.Thread(target=car,args=('本田车',))
        car1.start()
        light = threading.Thread(target=lighter,)
        light.start()
    

      

     8 应用场景

    python是伪多线程,实际上是单线程在context switch,所以不适合cpu密集型操作任务,而适合io密集型任务。(socket的多并发网络连接就是io密集型任务)

  • 相关阅读:
    离线安装SharePoint 2010必备组件下载链接
    skrollr——兼容性超强的视差滚动js插件
    Heroku实战入门(二)简单实战
    xp中安装sybase15.7遇到的三个问题
    思科三层交换机开启路由模式的方法
    Heroku实战入门(一)初识heroku
    Heroku实战入门(三)常用命令
    Splinter——开源的轻量级前端测试工具
    Request.ServerVariables 转
    Page.MaintainScrollPositionOnPostBack 属性
  • 原文地址:https://www.cnblogs.com/jabbok/p/9051619.html
Copyright © 2020-2023  润新知