• python 多线程


    python 多线程 threading

    一、多线程使用的的基本方式

    1、使用threading中现成的方法

    import threading
    
    def f1(arg):
        print(arg)
    
    t1 = threading.Thread(target=f1, args=(123,))
    t2 = threading.Thread(target=f1, args=(456,))
    t1.start()
    t2.start()

    说明:1、实例化一个Thead类就是生成一个线程。

         2、target是要执行的函数,args是函数的参数,以元组的形式提现。

             3、start函数代表线程已经准备好,等待CPU的调用。

    2、使用自定义的多线程类

    import threading
    
    class MyThread(threading.Thread):
        def __init__(self, func, arg):
            self._func = func
            self._arg = arg
            super(MyThread, self).__init__()
    
        def run(self):
            self._func(self._arg)
    
    t1 = MyThread(f2, 123)
    t.start()
    t2 = MyThread(f2, 456)
    t.start()

    说明:1、该类继承自threading.Thread类。

             2、自定义线程类需要重写run方法,run方法中就是该线程要做的事情。

             3、 如果需要重写构造方法__init__, 需要继承父类的构造方法调用super函数。

              4、start方法同上

    3、守护线程

    1)守护线程即守护主线程的线程,与主线程同生共死。

    2)主线程退出时,守护线程也跟着退出。

    3)默认情况下,创建的子线程非守护线程,既主线程如果执行完毕的,而子线程没有执行完毕,主线程会等待子线程执行完毕后再退出。

    4)设置守护线程的方法是 setDaemon = True。

    import threading
    import time
    
    def f1(arg):
        time.sleep(5)
        print(arg)
    
    t = threading.Thread(target=f1, args('我是守护线程'))
    t.setDaemon = True
    t.start()
    
    print('end')

    说明: 该程序中子线程设置为守护线程,主线程退出后,子线程也跟着退出(没有打印‘我是守护线程’)。

    4. join方法

    join方法会是主线程阻塞,直到使用join方法的子线程执行完毕后,继续往下执行。

    join方法还有个参数,设置阻塞的时间,默认情况下会无限阻塞下去。

    import threading
    import time
    
    def f1(arg):
        time.sleep(5)
        print(arg)
    
    t = threading.Thread(target=f1, args('123'))
    t.setDaemon = True
    t.join()
    
    print('end')

    说明:该程序会在执行到join方法的时候阻塞住,直到打印出123,再继续往下执行。

    5. 线程中的其他方法

    setName 为某个线程设置名字

    getName 获取某个线程的名字

    二、线程锁

    1.Lock

    由于线程使用相同的内存空间,可能在同一时间内去修改同一数据,对数据造成破坏,这种情况称作"线程不安全"。

    import threading
    import time
    
    class MyThread(threading.Thread):
        def run(self):
            global num
            time.sleep(1)
            num = num+1
            msg = self.name+' set num to '+str(num)
            print msg
    num = 0
    def test():
        for i in range(5):
            t = MyThread()
            t.start()
    if __name__ == '__main__':
        test()

    但是运行结果是不正确的

    Thread-5 set num to 2
    Thread-3 set num to 3
    Thread-2 set num to 5
    Thread-1 set num to 5
    Thread-4 set num to 4
    

      

    问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。既“线程不安全”。

    上面的程序引出了多线程编程的最常见问题:数据共享。当多个线程都修改某一个共享数据的时候,需要进行同步控制。

    线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

    import threading
    import time
    
    class MyThread(threading.Thread):
        def run(self):
            global num 
            time.sleep(1)
    
            if Lock.acquire(1):  
                num = num+1
                msg = self.name+' set num to '+str(num)
                print msg
                Lock.release()
    num = 0
    Lock = threading.Lock()
    def test():
        for i in range(5):
            t = MyThread()
            t.start()
    if __name__ == '__main__':
        test()
    运行结果:
    
    Thread-3 set num to 1
    Thread-4 set num to 2
    Thread-5 set num to 3
    Thread-2 set num to 4
    Thread-1 set num to 5
    

    可以看出使用了线程锁的程序结果复合预期效果。

    2、RLock

    Lock只能对某个线程锁一次,而RLock可以锁多次。使用方法同Lock

    3、信号量 Semaphore

    互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 。

    import threading
    import time
    n = 10
    
    def func(l, i):
        global n
        l.acquire()
        n -= 1
        time.sleep(1)
        print(n, i)
        l.release()
    
    lock = threading.BoundedSemaphore(5)
    for i in range(10):
        t = threading.Thread(target=func, args=(lock, i))
        t.start()
    输出结果:
    5 0
    4 1
    3 2  
    2 4
    2 3                          第一次(秒)输出结果:5个
    -------------------------
    0 5                          第二次(秒)输出结果:5个
    0 6
    0 7
    0 8
    0 9
    

    4、事件 event

    python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

    事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

    • clear:将“Flag”设置为False
    • set:将“Flag”设置为True
    #-*-coding:utf8-*-
    import threading
    
    def func(i,e):
        print(i)
        e.wait() # 检测是什么等,如果是红灯,停;绿灯,行
        print(i+100)
    
    event = threading.Event()
    
    for i in range(10):
        t = threading.Thread(target=func, args=(i,event,))
        t.start()
    #========
    event.clear() # 设置成红灯
    inp = input('>>>')
    if inp == "1":
        event.set() # 设置成绿灯
    输出结果:
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>>1 # 在此处阻塞住,输入数字1,将Flag设置成True
    101
    100
    103
    105
    107
    109
    102
    106
    108
    104
    

    5、条件Condition

    使得线程等待,只有满足某条件时,才释放n个线程

    import threading
    
    def run(n):
        con.acquire()
        con.wait()
        print("run the thread: %s" %n)
        con.release()
    
    if __name__ == '__main__':
    
        con = threading.Condition()
        for i in range(10):
            t = threading.Thread(target=run, args=(i,))
            t.start()
    
        while True:
            n = input('>>>')
            if inp == 'q':
                break
            con.acquire()
            con.notify(int(n))
            con.release()
    输出结果如下:
    
    >>>3
    >>>run the thread: 1
    run the thread: 2
    run the thread: 0
    3
    >>>run the thread: 3
    run the thread: 4
    run the thread: 5
    3
    >>>run the thread: 7
    run the thread: 8
    run the thread: 6
    3
    >>>run the thread: 9
    

    6、Timer 定时器

    定时器,指定n秒后执行某操作

    from threading import Timer
     
     
    def hello():
        print("hello, world")
     
    t = Timer(1, hello)
    t.start() 1s之后启动
  • 相关阅读:
    java总结1
    immutable
    iconfont
    实战build-react(三)+ style-components
    PHP判断字符串的包含
    win7 64位操作系统中 Oracle 11g 安装教程(图解)
    我在博客园写博客的原因
    面向对象程序设计-C++_课时17函数重载和默认参数
    面向对象程序设计-C++_课时16子类父类关系
    面向对象程序设计-C++_课时14对象组合_课时15继承
  • 原文地址:https://www.cnblogs.com/9527chu/p/5682644.html
Copyright © 2020-2023  润新知