102.多线程

相关概念

  • 程序:写好的代码,保存在硬盘里
  • 进程:进行中的程序,在内存中运行,相当于把硬盘中的程序挪到内存中,并执行。
  • 线程:一个进程中独立运行的片段,同一个进程可以有多个线程,共享同一个进程的数据和上下文运行环境
  • 全局解释器锁:在python主循环中只能有一个控制线程在执行

_thread包

  • 此包在python3中尽量不用
  • thread:有问题不用,python3中已经改成_thread
  • _thread.start_new_thread(函数名, 参数元组)
#_thread 例子
#问题1:主线程提前结束,导致分线程不一定能正常结束
#问题2:分线程运行顺序不定
import time
import _thread as thr

def loop1():
    print("loop1开始工作:", time.ctime())
    time.sleep(4)
    print("loop1结束工作:", time.ctime())

def loop2():
    print("loop2开始工作:", time.ctime())
    time.sleep(2)
    print("loop2结束工作:", time.ctime())

def main():
    print("主程序开始工作:", time.ctime())
    thr.start_new_thread(loop1, ())
    thr.start_new_thread(loop2, ())
    print("主程序结束工作:", time.ctime())

if __name__ == "__main__":
    main()

threading包

  • 在python3中,多数在用此包
  • threading.Thread(target=函数名, args=参数元组) 实例化线程
  • threading.Timer(3.0, 函数名,[参数]) 指定时间后执行函数
  • .start() 启动线程
  • .join() 等待子线程执行完毕

    #threading 例子
    import time
    import threadingr
    
    def loop1():
        print("loop1开始工作:", time.ctime())
        time.sleep(4)
        print("loop1结束工作:", time.ctime())
    
    def loop2():
        print("loop2开始工作:", time.ctime())
        time.sleep(2)
        print("loop2结束工作:", time.ctime())
    
    def main():
        print("主程序开始工作:", time.ctime())
        #第一个实例线程
        l1 = threading.Thread(target=loop1, args=())
        l1.start()
        #第二个实例线程
        l2 = threading.Thread(target=loop2, args=())
        l2.start()
        #主程序等待两个线程
        l1.join()
        l2.join()
    
        print("主程序结束工作:", time.ctime())
    
    if __name__ == "__main__":
        main()
  • .setDaemon(True)或.deamon=True 守护线程
    • 守护线程会在主线程结束时一个结束(和运行环境有关,不一定实现)
    • 在启动线程前进行设置
    #线程守护例子
    import time
    import threading
    
    def loop1():
        print("子线程开始")
        time.sleep(2)
        print("子线程结束")
    
    def main():
    
        print("主线程开始")
    
        l = threading.Thread(target=loop1, args=())
        l.setDaemon(True)
        l.start()
    
        print("主线程结束")
    
    if __name__ == "__main__":
        main()
  • threading常用属性
    • threading.currentThread() : 返回当前运行中线程变量(整体线程元素)
    • threading.enumerate() : 返回当前活动线程的数量,已列表形式
    • threading.activeCount() : 返回当前活动线程的数量,相当于len(threading.enumerate)
    • threading.setName() : 给线程设置名字
    • threading.getName() : 返回线程的名字
    import time
    import threading
    
    def loop1():
        print("线程1开始")
        time.sleep(2)
        print("线程1结束")
    
    def loop2():
        print("线程2开始")
        time.sleep(4)
        print("线程2结束")
    
    def loop3():
        print("线程3开始")
        time.sleep(6)
        print("线程3结束")
    
    def main():
    
        print("主线程开始")
    
        l1 = threading.Thread(target=loop1, args=())
        l1.setName("l1")
        l1.start()
    
        l2 = threading.Thread(target=loop2, args=())
        l2.setName("l2")
        l2.start()
    
        l3 = threading.Thread(target=loop3, args=())
        l3.setName("l3")
        l3.start()
    
        time.sleep(1)
    
        print("当前运行线程的参数的变量名:", threading.currentThread())
        print("当前活动线程数量:", threading.activeCount())
    
        for i in threading.enumerate():
            print(i.getName())
    
        print("主线程结束")
    
    if __name__ == "__main__":
        main()

另一种方式

  • threading.Thread子类方式
  • 子类可不写__init__函数,__但是如果写,就一定要调用父类的__init函数
  • 子类必须重新构run函数,run函数是执行内容,就是target参数所给函数的内容 .start()所要执行的内容

    import time
    import threading
    
    class T(threading.Thread):
    
        def __init__(self, arg):
            self.arg = arg
            #python2中用super(T, self).__init__()
            super().__init__()
    
        def run(self):
            time.sleep(2)
            print(self.arg)
    
    for i in range(5):
    
        l = T(i)
        l.start()
        l.join()
    
    print("主线程结束")

共享变量

多个线程同时操作一个变量

  • 由于操作顺序不定,导致结果不同
  • 解决方法:threading.lock()
import threading
import time

s = 0
ls = 100000

tlock = threading.Lock()

def jia():
    #global把局部变量变为全局变量
    global s, ls
    time.sleep(1)
    for i in range(1, ls):
        tlock.acquire()
        s += 1
        tlock.release()

def jian():
  
    global s, ls
    time.sleep(1)
    for i in range(1, ls):
        tlock.acquire()
        s -= 1
        tlock.release()

if __name__ == "__main__":
  
    j1 = threading.Thread(target=jia, args=())
    j2 = threading.Thread(target=jian, args=())

    j1.start()
    j2.start()

    j1.join()
    j2.join()

    print(s)

死锁问题

  • 解决办法threading.acquire(timeout=4) 超过四秒就不在申请
    • 若申请不到,就不能释放,此时做if处理
  • treading.semphore(3) 同时允许3个进程使用某变量

    import threading
    import time
    
    sem = threading.Semaphore(3)
    
    def func():
    
        if sem.acquire():      
    
            print(threading.currentThread().getName() + "获得线程锁")        
            time.sleep(10)
            sem.release()
            print(threading.currentThread().getName() + "释放线程所")
    
    for i in range(8):
        t = threading.Thread(target=func, args=())
        t.start()

可重入锁问题

  • 同一个线程多次申请锁的问题
  • 一般用于递归函数
  • threading.RLock()

    import threading
    import time
    
    class xiancheng(threading.Thread):
        def run(self):
            global a
            time.sleep(1)
            if loc.acquire(timeout=1):
                a = a + 1
                s = self.name + "设置数" + str(a)
                print(s)
                loc.acquire()
                loc.release()
                loc.release()
    
    def tx():
        for i in range(5):
            l = xiancheng()
            l.start()
    
    if __name__ == "__main__":
        a = 0
        loc = threading.RLock()#实例可重入锁
        tx()

变量安全

  • 不安全变量类型:list, set, dict等,可改变的变量类型
  • 安全变量:queue队列
    • queue.put(数据) 存入数据
    • queue.get() 去除数据
import queue
import time
import threading

class shengchanzhe(threading.Thread):
    def run(self):
        global q
        a = 0
        while True:
            if a < 1000:
                for i in range(100):
                    a = a + 1
                    b = "产品:" + str(a)
                    q.put(b)
                    print(b)
            time.sleep(0.5)

class xiaofeizhe(threading.Thread):
    def run(self):
        global q
        while True:
            if q.qsize() > 100:
                for i in range(5):
                    #线程有个.name属性,即线程名,可用.setName()和.getName()修改
                    b = self.name + "消费了" + q.get()
                    print(b)
            time.sleep(1)

      

if __name__ == "__main__":

    q = queue.Queue()
  
    for i in range(3):
        s = shengchanzhe()
        s.start()
  
    for i in range(5):
        x = xiaofeizhe()
        x.start()

线程的替代方案

  • subprocess
    • 完全跳过线程,使用进程
    • 派生进程的主要替代方式
    • python2.4后引入
  • multiprocessiong
    • 使用threading接口派生,使用子进程
    • 允许多核或多cpu派生进程,接口和threading极其相似
    • python2.6后引入
  • concurrent.futures
    • 新的异步执行模块
    • 任务级别的操作
    • python3.2后引入

猜你喜欢

转载自www.cnblogs.com/TK-tank/p/12311444.html
今日推荐