美文网首页
python多任务之线程

python多任务之线程

作者: rookieyu | 来源:发表于2019-06-09 15:34 被阅读0次

    线程概述

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

    使用线程可以把占据长时间的程序中的任务放到后台去处理。

    用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度

    程序的运行速度可能加快

    在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

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

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

    指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。

    线程可以被抢占(中断)。

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

    线程就是在程序运行过程中,执行程序代码的一个分支,每个运行的程序至少都有一个线程

    创建多线程:thread

    调用thread模块中的start_new_thread()函数来产生新线程

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

    function - 线程函数。

    args - 传递给线程函数的参数,他必须是个tuple类型。

    kwargs - 可选参数。

    python多线程的开启

    start()

    demo:

    import threading

    import time

    def task():

        time.sleep(1) 

         print("当前线程:", threading.current_thread().name)

    if__name__ =='__main__':

        for_inrange(5):

             sub_thread = threading.Thread(target=task) 

             sub_thread.start()

    线程之间执行是无序的

    主线程会等待所有的子线程结束后才结束

    主线程会等待所有的子线程结束后才结束,如果需要可以设置守护主线程

    多线程的特点(共享全局变量)

    threading.currentThread(): 返回当前的线程变量。

    threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。

    threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

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

    run(): 用以表示线程活动的方法。

    start():启动线程活动。

    join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。

    isAlive(): 返回线程是否活动的。

    getName(): 返回线程名。

    setName(): 设置线程名。

    自定义线程

    自定义线程不能指定target,因为自定义线程里面的任务都统一在run方法里面执行

    启动线程统一调用start方法,不要直接调用run方法, 因为这样不是使用子线程去执行任务

    demo:

    import threading

    # 自定义线程类

    classMyThread(threading.Thread):

    # 通过构造方法取接收任务的参数

        def__init__(self, info1, info2):

        # 调用父类的构造方法

            super(MyThread, self).__init__() 

             self.info1 = info1 

             self.info2 = info2

        # 定义自定义线程相关的任务

        def test1(self):

            print(self.info1)

        deftest2(self):

            print(self.info2)

        # 通过run方法执行相关任务

        def run(self):

            self.test1() 

             self.test2()

    # 创建自定义线程

    my_thread = MyThread("测试1","测试2")

    # 启动

    my_thread.start()

    资源竞争问题

    多线程可以共享全局变量

    多线程共享全局变量,很方便在多个线程间共享数据

    由于多线程同时对全局变量进行操作,很容易出现资源竞争问题

    demo:

    import threading

    # 定义全局变量

    g_num =0

    # 循环一次给全局变量加1

    defsum_num1():

        for i in range(1000000):

            globalg_num 

             g_num +=1

        print("sum1:", g_num)

    # 循环一次给全局变量加1

    defsum_num2():

        for i in range(1000000):

            global g_num 

             g_num +=1

        print("sum2:", g_num)

    if__name__ =='__main__':

    # 创建两个线程

    first_thread = threading.Thread(target=sum_num1) 

     second_thread = threading.Thread(target=sum_num2)

    # 启动线程

    first_thread.start()

    # 启动线程

    second_thread.start()

    我们可以看到多线程同时对全局变量操作数据发生了错误

    多线程同时操作全局变量导致数据可能出现错误的原因分析

    两个线程first_thread和second_thread都要对全局变量g_num(默认是0)进行加1运算,但是由于是多线程同时操作,有可能出现下面情况:

    在g_num=0时,first_thread取得g_num=0。此时系统把first_thread调度为”sleeping”状态,把second_thread转换为”running”状态,t2也获得g_num=0

    然后second_thread对得到的值进行加1并赋给g_num,使得g_num=1

    然后系统又把second_thread调度为”sleeping”,把first_thread转为”running”。线程t1又把它之前得到的0加1后赋值给g_num。

    这样导致虽然first_thread和first_thread都对g_num加1,但结果仍然是g_num=1

    多线程资源竞争解决办法

    线程同步

    线程同步: 保证同一时刻只能有一个线程去操作全局变量 同步: 就是协同步调,按预定的先后次序进行运行。如:你说完,我再说, 好比现实生活中的对讲机

    多个线程同时对同一个全局变量进行操作,会有可能出现资源竞争数据错误的问题

    线程同步方式可以解决资源竞争数据错误问题,但是这样有多任务变成了单任务。

    互斥锁

    threading模块中定义了Lock变量,这个变量本质上是一个函数,可以方便的处理锁定:

    # 创建锁

    mutex = threading.Lock()

    # 锁定

    mutex.acquire()

    # 释放

    mutex.release()

    demo:

    import threading

    # 定义全局变量

    g_num =0

    # 创建全局互斥锁

    lock = threading.Lock()

    # 循环一次给全局变量加1

    def sum_num1():

    # 上锁

        lock.acquire()

        for i in range(1000000):

            global g_num

            g_num +=1

        print("sum1:", g_num)

    # 释放锁

        lock.release()

    # 循环一次给全局变量加1

    def sum_num2():

    # 上锁

        lock.acquire()

        for iin range(1000000):

            global g_num

            g_num +=1

        print("sum2:", g_num)

    # 释放锁

        lock.release()

    if __name__ =='__main__':

    # 创建两个线程

        first_thread = threading.Thread(target=sum_num1)

    second_thread = threading.Thread(target=sum_num2)

    # 启动线程

        first_thread.start()

    second_thread.start()

    # 提示:加上互斥锁,那个线程抢到这个锁我们决定不了,那线程抢到锁那个线程先执行,没有抢到的线程需要等待

        # 加上互斥锁多任务瞬间变成单任务,性能会下降,也就是说同一时刻只能有一个线程去执行

    当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。

    每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。

    线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。

    确保了某段关键代码只能由一个线程从头到尾完整地执行

    锁的坏处:

    多线程执行变成了包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了

    锁使用不好就容易出现死锁情况

    死锁

    import threading

    import time

    # 创建互斥锁

    lock = threading.Lock()

    # 根据下标去取值, 保证同一时刻只能有一个线程去取值

    def get_value(index):

    # 上锁

        lock.acquire()

        print(threading.current_thread())

        my_list = [3,6,8,1]

    # 判断下标释放越界

        if index >=len(my_list):

            print("下标越界:", index)

            return

        value = my_list[index]

        print(value)

        time.sleep(0.2)

    # 释放锁

        lock.release()

    if __name__ =='__main__':

    # 模拟大量线程去执行取值操作

        for i in range(30):

            sub_thread = threading.Thread(target=get_value,args=(i,))

            sub_thread.start()

    使用互斥锁的时候需要注意死锁的问题,要在合适的地方注意释放锁

    死锁一旦发生就会造成应用的停止响应

    线程优先级队列( Queue)

    Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。

    Queue模块中的常用方法:

    Queue.qsize() 返回队列的大小

    Queue.empty() 如果队列为空,返回True,反之False

    Queue.full() 如果队列满了,返回True,反之False

    Queue.full 与 maxsize 大小对应

    Queue.get([block[, timeout]])获取队列,timeout等待时间

    Queue.get_nowait() 相当Queue.get(False)

    Queue.put(item) 写入队列,timeout等待时间

    Queue.put_nowait(item) 相当Queue.put(item, False)

    Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号

    Queue.join() 实际上意味着等到队列为空,再执行别的操作

    相关文章

      网友评论

          本文标题:python多任务之线程

          本文链接:https://www.haomeiwen.com/subject/dswgxctx.html