当前位置 : 主页 > 编程语言 > python >

python中的多线程

来源:互联网 收集:自由互联 发布时间:2022-07-05
线程和进程 进程是资源分配的最小单位。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于追踪执行的辅助数据。操作系统管理其上所有进程的执行,并为这些进程合理地分

线程和进程

进程是资源分配的最小单位。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于追踪执行的辅助数据。操作系统管理其上所有进程的执行,并为这些进程合理地分配时间。进程也可以通过派生(fork或spawn)新的进程来执行其他任务,不过因为每个新进程也都拥有自己的内存和数据栈等,所以只能采用进程间通信(IPC)的方式共享信息
线程是程序执行的最小单位。线程与进程类似,不过它们是在同一个进程下执行的,并享有相同的上下文。线程包括开始、执行顺序和结束三部分。它有一个指令指针,用于记录当前运行的上下文。当其他线程运行时,它可以被抢占(中断)和临时挂起(也称为睡眠)——这种做法叫做让步(yielding)
一个进程中的各个线程与主线程共享同一片数据空间,因此相比于独立的进程而言,线程间的信息共享和通信更加容易。线程一般都是以并发方式执行,正是由于这种并行和数据共享进制,使得多任务间的协作称为了可能

全局解释器锁

Python代码的执行是由Python虚拟机进程控制的,对Python虚拟机的访问是由全局解释器锁(GIL)控制的。尽管Python解释器中可以运行多个线程,但是有个这个锁,在任意给定时刻只有一个线程会被解释器执行

退出线程

当一个线程完成函数的执行时,它就会退出。不建议使用thread模块的一个明显的原因是:在主线程退出之后,所有其他线程都会在没有清理的情况下直接退出,而另一个模块threading会确保在所有"重要的"子线程退出前,保证整个进程的存活

thread模块

thread模块除了派生线程外,还提供了基本的同步数据结构,称为锁对象。其主要的线程方法和锁对象方法有:

thread 模块的方法

描述

start_new_thread(function, args, kwargs=None)

派生一个新的线程,使用给定的args和可选的kwargs来执行function

allocate_lock()

分配LockType锁对象

exit()

给线程退出指令

LockType锁对象的方法

描述

acquire(wait=None)

尝试获取锁对象

locked()

如果获取了锁对象则返回True,否则返回False

release()

释放锁

在下面的例子中,为了实现线程的同步,要对每一个线程都加锁,执行完之后再释放锁,最后一个循环暂停主线程,直到所有锁都被释放后才可以继续执行

import _thread as thread
from time import sleep, ctime

loops = [4, 2] #sleep时间

def loop(nloop, nsec, lock):
print("start loop", nloop, "at:", ctime())
sleep(nsec)
print("loop", nloop, "done at:", ctime())
lock.release() #释放锁,每个线程执行完,都会释放自己的锁

def main():
print("starting at:", ctime())
locks = [] #创建一个锁列表
nloops = range(len(loops)) #range(2)

for i in nloops:
lock = thread.allocate_lock() #获取锁对象
lock.acquire() #获得每个锁,效果相当于"把锁锁上"
locks.append(lock) #将锁添加到锁列表中

for i in nloops:
thread.start_new_thread(loop, (i, loops[i], locks[i]))

for i in nloops:
while locks[i].locked():
pass #等待(暂停主线程),直到所有锁被释放后才会继续执行

print("all DONE at:", ctime())


if __name__ == "__main__":
main()

threading模块

threading模块提供了更高级别、功能更全面的线程管理。threading模块的Thread类是主要的执行对象,它有thread模块中没有的很多函数

Thread对象数据属性

描述

name

线程名

ident

线程的标识符

daemon

布尔标志,表示这个线程是否是守护线程

| Thread对象方法 | 描述 |
| init(group=None, target=None, name=None, args=(), kwargs={}. verbose=None, daemon=None) | 实例化一个线程对象,需要有一个可调用的target,以及参数args或kwargs,还可以传递name或group参数,不过后者还未实现。此外,verbose标志也是可接受的,而daemon的值会设定thread.daemon属性/标志 |
| start() | 开始执行该线程 |
| run() | 定义线程功能的方法(通常在子类中被应用开发者重写) |
| join(timeout=None) | 直至启动的线程终止之前一直挂起,除非给出了timeout(秒),否则会一直阻塞 |

使用Thread类,可以有很多方法来创建线程。这里说其中的两种方法:

  • 创建Thread的实例,传给它一个函数
  • 派生Thread的子类,并创建子类的实例

创建Thread的实例

在下面的例子中,做了哪些修改呢?使用thread实现的锁没了,取而代之的是一组Thread对象。当实例化每个Thread对象时,把函数(target)和参数(args)传进去,然后得到返回的Thread实例。调用Thread()实例化Thread和调用thread.start_new_thread()的最大区别是新线程不会立即开始执行
当所有线程都分配完成之后,通过调用每个线程的start()方法让它们开始执行,相比于管理一组锁(分配、获取、释放、检查锁状态等)而言,这里只需要为每个线程调用join()方法即可。join()方法将等待线程结束,或者在提供了超时时间的情况下,达到超时时间。使用join()方法要比等待锁释放的无限循环更加清晰。如果主线程还有其他的事情要做,而不是等待这些线程完成,就可以不调用join()

import threading
from time import sleep, ctime

loops = [4, 2]

def loop(nloop, nsec):
print("start loop", nloop, "at:", ctime())
sleep(nsec)
print("loop", nloop, "done at:", ctime())


def main():
print("starting at:", ctime())
threads = []
nloops = range(len(loops))

for i in nloops:
t = threading.Thread(target=loop, args=(i, loops[i]))
threads.append(t)

for i in nloops:
threads[i].start()

for i in nloops:
threads[i].join()

print("all DONE at:", ctime())


if __name__ == "__main__":
main()

派送Thread的子类

派送出来的子类MyThread继承了父类Thread的构造方法,同时又新增了自己独有的实例属性

import threading
from time import sleep, ctime

loops = [4, 2]

class MyThread(threading.Thread):

def __init__(self, func, args, name=""):
threading.Thread.__init__(self)
self.name = name
self.func = func
self.args = args

def run(self):
self.func(*self.args)


def loop(nloop, nsec):
print("start loop", nloop, "at:", ctime())
sleep(nsec)
print("loop", nloop, "done at:", ctime())


def main():
print("starting at:", ctime())
threads = []
nloops = range(len(loops))

for i in nloops:
t = MyThread(loop, (i, loops[i]), loop.__name__)
threads.append(t)

for i in nloops:
threads[i].start()

for i in nloops:
threads[i].join()

print("all DONE at:", ctime())


if __name__ == "__main__":
main()

还可以对MyThread类进程修改,增加一些调试信息的输出,并将其存储为一个名为myThread的独立模块,以便后面的模块引入调用。除了简单的调用函数外,还可以把结果保存在实例属性self.res,并创建一个新的方法getResult()来获取这个值

import threading
from time import ctime

class MyThread(threading.Thread):

def __init__(self, func, args, name=""):
threading.Thread.__init__(self)
self.name = name
self.func = func
self.args = args


def getResult(self):
return self.res


def run(self):
print("starting", self.name, "at:", ctime())
self.res = self.func(*self.args)
print(self.name, "finished at:", ctime())


上一篇:python中*和**的打包和解包
下一篇:没有了
网友评论