zoukankan      html  css  js  c++  java
  • Python多线程_thread和Threading

    目录

    多线程

    _thread模块

    使用 _thread模块创建线程

    threading

    使用 threading模块创建线程

    线程同步 


    在讲多线程之前,我们先看一个单线程的例子:

    import _thread
    import time
    from datetime import datetime
    
    def Test(name):
        for i in range(3):
            print(name,datetime.now())
            time.sleep(1)
    def main():
        Test("one")
        Test("two")
    
    if __name__=='__main__':
        start=time.time()
        main()
        end=time.time()
        print("运行程序花费了%s秒"%(end-start))
    ##################################################
    one 2018-11-11 11:17:57.984571
    one 2018-11-11 11:17:58.987543
    one 2018-11-11 11:17:59.988000
    two 2018-11-11 11:18:00.988495
    two 2018-11-11 11:18:01.989449
    two 2018-11-11 11:18:02.990809
    运行程序花费了6.008334159851074秒

    可以看到,对于单线程,运行完这段程序需要6秒的时间。

    现在我们开始讲多线程了!

    Python3通过两个标准库 _thread threading 提供对线程的支持。

    _thread 提供了低级别的、原始的线程以及一个简单的锁,它相比于 threading 模块的功能还是比较有限的。threading 模块除了包含 _thread 模块中的所有方法外,还提供其他方法。所以我们一般常用的是 threading 模块

    多线程

    多线程类似于同时执行多个不同程序,多线程运行有如下优点:

    • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
    • 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
    • 程序的运行速度可能加快
    • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

    线程在执行过程中与进程还是有区别的。每个独立的进程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,线程必须依存在进程中,由应用程序提供多个线程执行控制。

    每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了进程上次运行该线程的CPU寄存器的状态。

    • 线程可以被抢占(中断)。
    • 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让

    Python中使用线程有两种方式函数 或者用 来包装线程对象。_thread 模块用的是函数式线程,threading 可以包装线程对象,也可以使用函数式线程

    _thread模块(不常用)

    函数式:调用 _thread 模块中的 start_new_thread() 函数来产生新线程。语法如下:

    _thread.start_new_thread ( function, args [, kwargs] )

    参数说明:

    • function - 线程函数
    • args - 传递给线程函数的参数,他必须是个 tuple 类型
    • kwargs - 可选参数

    使用 _thread模块创建多线程

    对于上面这个例子,我们用多线程来实现,看需要多长的时间

    import _thread
    import time
    from datetime import datetime
    
    def Test(name):
        for i in range(3):
            print(name,datetime.now())
            time.sleep(1)
    def main():
            _thread.start_new_thread(Test,("one ",))
            _thread.start_new_thread(Test,("two ",))
    
    if __name__=='__main__':
        start=time.time()
        main()
        end=time.time()
        print("运行程序花费了%s秒"%(end-start))
    ############################################################
    运行程序花费了0.0秒two  2018-11-11 11:30:53.353519
    one 
     2018-11-11 11:30:53.360632
    two  2018-11-11 11:30:54.361750
    one  2018-11-11 11:30:54.368121
    two  2018-11-11 11:30:55.364558
    one  2018-11-11 11:30:55.369716

    咦,为什么程序花费0.0秒呢?而且是第一个打印呢?原因在于当子线程还在sleep的时候,我们的父线程就已经执行完了。父线程并没有等待说让子线程执行完再执行。所以最后打印程序花费的时间是0.0秒。但是看子线程打印出来的东西我们可以看到,运行程序是花费了3秒时间的,比单线程省了一半的时间。

    这里父线程并没有等待说子线程执行完再执行,这里就涉及到了一个阻塞问题。阻塞问题我们在 threading模块中讲解,利用 threading模块中的 join() 方法来阻塞。 

    threading

    threading 模块除了包含 _thread 模块中的所有方法外,还提供其他方法:

    • threading.currentThread(): 返回当前的线程变量。
    • threading.enumerate():  返回一个包含正在运行的线程的list列表。正在运行指线程启动后、结束前。
    • threading.activeCount(): 返回正在运行的线程数量,与 len(threading.enumerate()) 有相同的结果。

    除了使用方法外,threading 线程模块同样提供了 threading.Thread 类来处理线程,threading.Thread类提供了以下方法:

    • run(): 用以表示线程活动的方法。
    • start():启动线程活动。
    • join([time]): 父线程等待子线程至中止再执行。这阻塞调用线程直至线程的join() 方法被调用中止、正常退出或者抛出未处理的异常、再或者是可选的超时发生。
    • isAlive(): 返回线程是否活动的。
    • getName():  返回线程名。
    • setName():  设置线程名。

    使用 threading模块创建多线程

    我们可以通过创建一个子类继承于 threading.Thread ,并实例化后调用 start() 方法启动新线程,即它调用了线程的 run() 方法:

    我们这里使用了 join()方法来阻塞父线程,让他等待子线程运行完再运行,可以看到,最后我们花费了3秒的时间,是单线程的一半。

    import threading
    import time
    from datetime import datetime
    
    class myThread(threading.Thread):
        def __init__(self,name):
            threading.Thread.__init__(self)
            self.name=name
        def run(self):
            for i in range(3):
                print(self.name,datetime.now())
                time.sleep(1)
    def main():
        thread1=myThread("one ")
        thread2=myThread("two ")
        thread1.start()           #启动线程,代用了该线程的 run() 方法
        thread2.start()
        thread1.join()       #必须等子线程运行完了父线程才可以运行
        thread2.join()   
    if __name__=='__main__':
        start=time.time()
        main()
        end=time.time()
        print("运行程序花费了%s秒"%(end-start))
    ###########################################
    one  2018-11-11 16:14:50.546845
    two  2018-11-11 16:14:50.554877
    one  2018-11-11 16:14:51.555091
    two  2018-11-11 16:14:51.563160
    one  2018-11-11 16:14:52.556510
    two  2018-11-11 16:14:52.564490
    运行程序花费了3.0182247161865234秒

    我们也可以使用函数来创建多线程 

    import threading
    import time
    from datetime import datetime
    
    def Test(name):
        for i in range(3):
            print(name,datetime.now())
            time.sleep(1)
    def main():
            t1=threading.Thread(target=Test,args=("one",))  #调用threading.Thread函数,target参数是要执行的函数,args是要传入的参数,为元组类型
            t2=threading.Thread(target=Test,args=("two",))
            t1.start()        #启动线程
            t2.start()
            t1.join()       #必须等子线程运行完了父线程才可以运行
            t2.join()
    if __name__=='__main__':
        start=time.time()
        main()
        end=time.time()
        print("运行程序花费了%s秒"%(end-start))
    ##########################################################
    one 2018-11-11 16:18:54.727638
    two 2018-11-11 16:18:54.735688
    one 2018-11-11 16:18:55.736727
    two 2018-11-11 16:18:55.743748
    one 2018-11-11 16:18:56.740741
    two 2018-11-11 16:18:56.746846
    运行程序花费了3.0212459564208984秒

    线程同步 

    如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。

    使用 threading 对象的 LockRlock 可以实现简单的线程同步,这两个对象都有 acquire() 方法和 release() 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire() 和 release() 方法之间。如下:

    多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。

    考虑这样一种情况:

    一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。

    那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。

    锁有两种状态——锁定和未锁定。每当一个线程比如"set"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞;等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。

    经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。

    实例:

    import threading
    import time
    from datetime import datetime
    threadLock=threading.Lock()  #得到一个threadLock对象
    
    def Test(name):
        threadLock.acquire()       ##获取锁,用于线程同步
        for i in range(3):
            print(name,datetime.now())
            time.sleep(1)
        threadLock.release()      #释放锁,开启下一个线程
    def main():
            t1=threading.Thread(target=Test,args=("one",))  #调用threading.Thread函数,target参数是要执行的韩三户,args是要传入的参数,为元组类型
            t2=threading.Thread(target=Test,args=("two",))
            t1.start()        #启动线程
            t2.start()
            t1.join()       #必须等子线程运行完了父线程才可以运行
            t2.join()
    if __name__=='__main__':
        start=time.time()
        main()
        end=time.time()
        print("运行程序花费了%s秒"%(end-start))
    ##################################################################
    one 2018-11-11 16:21:42.749223
    one 2018-11-11 16:21:43.757153
    one 2018-11-11 16:21:44.761436
    two 2018-11-11 16:21:45.764834
    two 2018-11-11 16:21:46.788177
    two 2018-11-11 16:21:47.791732
    运行程序花费了6.047051906585693秒

    从上面代码可以看出,对函数进行了线程锁定。这样当我们的线程1和线程2都同时调用该函数时, 必须等线程1执行完了线程2再执行,这样,程序花费的时间就相当于单线程时候的6秒了。

  • 相关阅读:
    sqli_labs less-2
    sqli_labs less-1
    我与西瓜书2外传----More about LinearRegression
    我与西瓜书2----线性模型
    我与西瓜书1-----绪论,模式评估与选择
    PE文件
    python1----variable,condition,function and loop
    文件(file)2————inode简介与文件的读取
    文件(file)1————An introduction to Linux filesystems
    bash4----进阶1 内部变量
  • 原文地址:https://www.cnblogs.com/csnd/p/11807840.html
Copyright © 2011-2022 走看看