龙空技术网

Python 3 高级编程 - 多线程编程

逸剑听潮 1029

前言:

此刻咱们对“python 多线程 全局变量”可能比较看重,朋友们都需要分析一些“python 多线程 全局变量”的相关知识。那么小编同时在网络上网罗了一些关于“python 多线程 全局变量””的相关内容,希望姐妹们能喜欢,同学们快快来了解一下吧!

python中的多线程是一个非常重要的知识点,python 默认是单线程的。

什么是线程:线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。线程是进程中的一个实体,是CPU调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行线程与进程的区别

进程(process)和线程(thread)是操作系统的基本概念,“进程是资源分配的最小单位,线程是CPU调度的最小单位”。线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

进程是资源分配的基本单位。所有与该进程有关的资源,都被记录在进程控制块PCB中。以表示该进程拥有这些资源或正在使用它们。进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。

线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。线程只由相关堆栈(系统栈或用户栈)寄存器和线程控制表TCB组成。

通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。

主线程:当⼀个程序启动时,就有⼀个进程被操作系统(OS)创建,与此同时⼀个线程也⽴刻运 ⾏,该线程通常叫做程序的主线程。

主线程的重要性:

是产生其他子线程的线程通常它必须最后完成执行各种关闭动作

子线程:可以看做是程序执⾏的⼀条分⽀,当⼦线程启动后会和主线程⼀起同时执⾏。

线程

一个线程有一个开始、一个执行顺序和一个结束。它有一个指令指针,可以跟踪它当前在上下文中的哪个位置运行。

它可以被抢占(中断)。它可以在其他线程运行时暂时搁置(也称为休眠)——这称为让步。

有两种不同的线程:

kernel thread 内核线程user thread 用户线程

内核线程是操作系统的一部分,而用户空间线程并未在内核中实现。

Python 提供了多个模块来支持多线程编程,包括 thread、 threading 和 Queue 模块等。程序是可以使用 thread 和 threading 模块来创建与管理线程。 thread 模块提供了基本的线程和锁定支持;而 threading 模块提供了更高级别、功能更全面的线程管理。

Python3 中有两个模块支持使用线程:

_threadthreading

在 Python 3 中,“thread”模块不再可用。为了在 Python3 中向后兼容,它已重命名为“_thread”。

创建一个新线程

要创建一个新线程,需要调用线程模块中的方法:

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

此方法调用支持在 Linux 和 Windows 中快速高效地创建新线程。方法调用立即返回,子线程启动并使用传递的参数列表调用函数。当函数返回时,线程终止。

args 是一个参数元组;使用空元组调用函数将不传递任何参数。 kwargs 是可选字典,是关键字参数。

例如:华罗庚先生的时间统筹法烧水,其实就是一个单线程和多线程的例子。

如果按照线性思维,先去洗开水壶、烧水花去15分钟,再去洗茶壶、洗茶杯和拿茶叶要10分钟,那一共需要25分钟按照时间统筹法,先洗开水壶,把水放在炉子上烧,然后同时去洗茶壶、茶杯、拿茶叶,等水烧好了,茶具也准备好了,这样两件事情一共只需要花费15分钟,无形中你就节约了10分钟的时间。

单线程:

#单线程import timedef boiling():    print('我要烧开水了')    time.sleep(40)    #模拟需要一定的时间    print('水烧开了')def wash_port():    print('我要洗茶壶茶杯')    time.sleep(20)    print('茶壶洗好了')def wash_cup():    print('我要洗茶杯')    time.sleep(10)    print('茶杯洗好了')start_time = time.time()boiling()wash_port()wash_cup()end_time = time.time()print('总共耗时:{}'.format(end_time-start_time)) 

运行结果:

我要烧开水了水烧开了我要洗茶壶茶杯茶壶洗好了我要洗茶杯茶杯洗好了总共耗时:70.03596377372742

多线程:

import _threadimport timedef boiling():    print('我要烧开水了')    time.sleep(40)    print('水烧开了')def wash_port():    print('我要洗茶壶茶杯')    time.sleep(20)    print('茶壶洗好了')def wash_cup():    print('我要洗茶杯')    time.sleep(10)    print('茶杯洗好了')start_time = time.time()try:    _thread.start_new_thread( boiling,())    _thread.start_new_thread( wash_port,())    _thread.start_new_thread( wash_cup,())except:   print ("Error: unable to start thread")finally:    end_time = time.time()    print('总共耗时:{}'.format(end_time-start_time)) while 1:   #一直等待,等待子线程运行完   pass

运行结果:

总共耗时:0.0  #主线程,运行完了,子线程还在运行我要烧开水了我要洗茶杯我要洗茶壶茶杯茶杯洗好了茶壶洗好了水烧开了

程序进入无限循环。您必须按 ctrl-c 才能停止。

Threading 线程模块

Python 2.4 中包含的较新的线程模块为线程提供了比 _thread 线程模块更强大、更高级的支持。

threading 模块公开了 thread 模块的所有方法并提供了一些额外的方法:

threading.activeCount() - 返回活动线程对象的数量。threading.currentThread() - 返回调用者线程控件中线程对象的数量。threading.enumerate() - 返回当前活动的所有线程对象的列表。

threading 模块还有实现线程的Thread 类。 Thread 类提供的方法如下:

run() - 是线程的入口点。start() - 通过调用 run 方法启动线程。join([time]) - 等待线程终止。表示主线程等待子线程time时长(s)后子线程若还未结束,就强制结束子线程,不设置则主线程会一直等待子线程结束后再结束。isAlive() - 检查线程是否仍在执行。getName() - 返回线程的名称。setName() - 设置线程的名称。daemon 属性- 设置主,子线程运行时的关系。bool为True时主线程结束,子线程立即结束;为false主,子线程运行毫不相关,各自独立运行。

使用 threading 线程模块创建线程

创建线程之前,需要先把交给线程去做的工作写成一个函数,这个函数叫线程函数。

操作过程如下:

定义 Thread 类的新子类。覆盖 __init__(self [,args]) 方法以添加额外的参数。然后,重写 run(self [,args]) 方法来实现线程在启动时应该做什么。

一旦创建了新的 Thread 子类,就可以创建它的一个实例,然后通过调用 start() 启动一个新线程,而 start() 又会调用 run() 方法。

import threadingimport timeexitFlag = 0#类必须继承threading.Threadclass myThread (threading.Thread):	 #threadID, name, counter 为传入线程的参数,可根据自己的需求进行定义   def __init__(self, threadID, name, counter):      # Thread类的__init__(self)方法      threading.Thread.__init__(self)      self.threadID = threadID      self.name = name      self.counter = counter   # 定义run()方法,主要写线程的执行内容   def run(self):      print ("Starting " + self.name)      print_time(self.name, self.counter, 5)      print ("Exiting " + self.name)def print_time(threadName, delay, counter):   #线程函数   while counter:      if exitFlag:         threadName.exit()      time.sleep(delay)      print ("%s: %s" % (threadName, time.ctime(time.time())))      counter -= 1# 创建两个线程thread1 = myThread(1, "Thread-1", 1)thread2 = myThread(2, "Thread-2", 2)# 启动线程thread1.start()thread2.start()#等待线程终止thread1.join()thread2.join()print ("Exiting Main Thread")

运行结果:

Starting Thread-1Starting Thread-2Thread-1: Mon Apr  3 11:42:02 2023Thread-1: Mon Apr  3 11:42:03 2023Thread-2: Mon Apr  3 11:42:03 2023Thread-1: Mon Apr  3 11:42:04 2023Thread-2: Mon Apr  3 11:42:05 2023Thread-1: Mon Apr  3 11:42:05 2023Thread-1: Mon Apr  3 11:42:06 2023Exiting Thread-1Thread-2: Mon Apr  3 11:42:07 2023Thread-2: Mon Apr  3 11:42:09 2023Thread-2: Mon Apr  3 11:42:11 2023Exiting Thread-2   Exiting Main Thread

注意:创建的线程为子线程,是主线程创建的子线程。

线程同步

Python 提供的线程模块包括一个易于实现的锁定机制,允许同步线程。通过调用 Lock() 方法创建一个新锁,该方法返回新锁。新锁对象的 acquire(blocking) 方法用于强制线程同步运行。可选的阻塞参数使您能够控制线程是否等待获取锁。如果阻塞设置为 0,则如果无法获取锁,线程将立即返回 0 值,如果获取了锁,则返回 1。如果 blocking 设置为 1,则线程阻塞并等待锁被释放。新锁对象的 release() 方法用于在不再需要时释放锁。

import threadingimport timeclass myThread (threading.Thread):   def __init__(self, threadID, name, counter):      threading.Thread.__init__(self)      self.threadID = threadID      self.name = name      self.counter = counter   def run(self):      print ("Starting " + self.name)      # 加锁进行线程同步      threadLock.acquire()      print_time(self.name, self.counter, 3)      # 释放锁      threadLock.release()def print_time(threadName, delay, counter):   while counter:      time.sleep(delay)      print ("%s: %s" % (threadName, time.ctime(time.time())))      counter -= 1threadLock = threading.Lock()threads = []# 创建新线程thread1 = myThread(1, "Thread-1", 1)thread2 = myThread(2, "Thread-2", 2)# 启动线程thread1.start()thread2.start()# 将线程添加到线程列表threads.append(thread1)threads.append(thread2)# 等待所有线程运行完毕for t in threads:   t.join()print ("Exiting Main Thread")

运行结果:

Starting Thread-1Starting Thread-2Thread-1: Mon Apr  3 11:51:44 2023Thread-1: Mon Apr  3 11:51:45 2023Thread-1: Mon Apr  3 11:51:46 2023Thread-2: Mon Apr  3 11:51:48 2023Thread-2: Mon Apr  3 11:51:50 2023Thread-2: Mon Apr  3 11:51:52 2023Exiting Main Thread   
多线程优先级队列

Queue 模块允许创建一个可以容纳特定数量项目的新队列对象。有以下方法来控制队列 -

get() - get() 从队列中删除并返回一个项目。put() - put 将项目添加到队列中。qsize() - qsize() 返回队列中当前的项目数。empty() - 如果队列为空,则 empty() 返回 True;否则,假。full() − 如果队列已满,full() 返回 True;否则,假。

import queueimport threadingimport timeexitFlag = 0class myThread (threading.Thread):   def __init__(self, threadID, name, q):      threading.Thread.__init__(self)      self.threadID = threadID      self.name = name      self.q = q   def run(self):      print ("Starting " + self.name)      process_data(self.name, self.q)      print ("Exiting " + self.name)def process_data(threadName, q):   while not exitFlag:      queueLock.acquire()      if not workQueue.empty():         data = q.get()         queueLock.release()         print ("%s processing %s" % (threadName, data))      else:         queueLock.release()         time.sleep(1)threadList = ["Thread-1", "Thread-2", "Thread-3"]nameList = ["One", "Two", "Three", "Four", "Five"]queueLock = threading.Lock()workQueue = queue.Queue(10)threads = []threadID = 1# Create new threadsfor tName in threadList:   thread = myThread(threadID, tName, workQueue)   thread.start()   threads.append(thread)   threadID += 1# Fill the queuequeueLock.acquire()for word in nameList:   workQueue.put(word)queueLock.release()# Wait for queue to emptywhile not workQueue.empty():   pass# Notify threads it's time to exitexitFlag = 1# Wait for all threads to completefor t in threads:   t.join()print ("Exiting Main Thread")

运行结果:

Starting Thread-1Starting Thread-2Starting Thread-3Thread-1 processing OneThread-2 processing TwoThread-3 processing ThreeThread-1 processing FourThread-2 processing FiveExiting Thread-3Exiting Thread-1Exiting Thread-2Exiting Main Thread
守护线程

Daemon属性用于设置主线程的守护线程,在启动线程启动之前使用。当bool为True时,该线程为守护线程,主线程结束,子线程立即结束;为false主,子线程运行毫不相关,独立运行,子线程继续运行到结束。

例如,daemon = False:

import timeimport threadingdef sub_thread():   print("子线程开始 ...")   time.sleep(3)   print("子线程结束 ...")print("主线程开始 ...")t = threading.Thread(target=sub_thread)t.daemon = Falset.start()print("主线程已结束 ...")

运行结果:

主线程开始 ...子线程开始 ...  主线程已结束 ...子线程结束 ...

例如,daemon = True:

import timeimport threadingdef sub_thread():   print("子线程开始 ...")   time.sleep(3)   print("子线程结束 ...")print("主线程开始 ...")t = threading.Thread(target=sub_thread)t.daemon = Truet.start()print("主线程已结束 ...")

运行结果:

主线程开始 ...子线程开始 ...  主线程已结束 ...

主线程结束时,子线程也立即被结束了。

线程锁Lock锁

threading模块中Lock锁和_thread模块中的锁是一样的。

import threadingimport timenum = 0# 申请线程锁lock = threading.Lock()# 类必须继承threading.Threadclass threadTest(threading.Thread):    def __init__(self):        threading.Thread.__init__(self)    # 定义run()方法,主要写线程的执行内容    def run(self):        # 声明全局变量num        global num        # 申请线程锁        lock.acquire()        print('子线程' + self.name + '开始: ' +              time.strftime('%T', time.localtime()))        while num < 5:            time.sleep(2)            print(self.name, 'num: ', num)            num += 1        print('子线程' + self.name + '结束: ' +              time.strftime('%T', time.localtime()))        # 释放线程锁        lock.release()        return super().run()print('主线程开始: %s' % (time.strftime('%T', time.localtime())))thread1 = threadTest()# 设置线程名称thread1.name = 'Thread-1'thread2 = threadTest()# 设置线程名称thread2.name = 'Thread-2'# 启动线程thread1.start()# 启动线程thread2.start()time.sleep(1)thread1.join()thread2.join()print('主线程已结束: %s' % (time.strftime('%T', time.localtime())))

运行结果:

主线程开始: 13:13:30子线程Thread-1开始: 13:13:30Thread-1 num:  0Thread-1 num:  1Thread-1 num:  2Thread-1 num:  3Thread-1 num:  4子线程Thread-1结束: 13:13:40子线程Thread-2开始: 13:13:40子线程Thread-2结束: 13:13:40主线程已结束: 13:13:40

在使用锁线程时,线程1和线程2对num操作就不会出现混乱。

RLock锁

RLock锁又称递归锁,其与Lock锁的差别在于,Lock锁只允许在同一线程中申请一次,否则线程会进入死锁,但是RLock允许在同一线程多次调用。

使用Lock锁产生死锁示例代码:

import threadingimport timeprint('主线程开始: %s' %(str(time.strftime('%T', time.localtime()))))lock = threading.Lock()# 申请线程锁lock.acquire()print(threading.enumerate())# 再次申请线程锁,产生了死锁lock.acquire()print(threading.enumerate())lock.release()lock.release()print('主线程结束: %s' %(str(time.strftime('%T',time.localtime()))))

运行结果:

主线程开始: 13:17:04[<_MainThread(MainThread, started 31564)>]

线程锁死。

使用RLock锁不会产生死锁示例代码:

import threadingimport timeprint('主线程开始: %s' %(str(time.strftime('%T', time.localtime()))))lock = threading.RLock()# 申请线程锁lock.acquire()print(threading.enumerate())# 再次申请线程锁,不会产生死锁lock.acquire()print(threading.enumerate())lock.release()lock.release()print('主线程结束: %s' %(str(time.strftime('%T',time.localtime()))))

运行结果:

主线程开始: 13:19:07[<_MainThread(MainThread, started 32932)>][<_MainThread(MainThread, started 32932)>]主线程结束: 13:19:07

注意线程锁需要成对出现。

条件变量 Condition

条件变量(Condition)也是一把锁,除了同步锁的作用外,还具有在线程间通信的功能。  有一类线程需要满足条件之后才能够继续执行,Python提供了threading.Condition 对象用于条件变量线程的支持。

Condition是python3中一种更高级的锁,除和线程锁类似的 acquire() 和 release() 函数外,还提供以下函数:

wait() 条件不满足时调用,线程会释放锁并进入等待阻塞;notify() 条件创造后调用,通知等待池激活一个线程;notifyAll() 条件创造后调用,通知等待池激活所有线程。

注意:线程使用前需要获得锁,否则会抛出RuntimeError异常。

Condition提供了一种多线程通信机制,若线程1需要数据,线程1就会阻塞等待,线程2制造出数据,等待线程2制造好数据并通知线程1后,线程1就可以去获取数据了。

import threading,timefrom random import randintclass Producer(threading.Thread):    def run(self):        global L        while True:            val=randint(0,10)            print('生产者',self.name,":Append"+str(val),L)            if lock_con.acquire():                L.append(val)                lock_con.notify()   #通知消费者,激活wait。                lock_con.release()            time.sleep(3)            class Consumer(threading.Thread):    def run(self):        global L        while True:            lock_con.acquire()  #wait阻塞后,从这里开始这行,重新获得锁。            if len(L)==0:          #如果容器中没有数据,则等待。                lock_con.wait()  #wait的作用:1、释放锁;2、阻塞,等待notify通知            print('消费者',self.name,":Delete"+str(L[0]),L)            del L[0]            lock_con.release()            time.sleep(1)if __name__=="__main__":    L=[]  #容器    lock_con=threading.Condition()  #创建一把条件同步变量的锁。    threads=[]    for i in range(3):        threads.append(Producer())    threads.append(Consumer())    for t in threads:        t.start()       #start了4个线程对象。    for t in threads:        t.join()    print('-------程序结束----------')

标签: #python 多线程 全局变量 #python3高级用法 #python进程休眠