进程
计算机程序只是存储在磁盘上的可执行二进制(或其他类型)文件。只有把它们加载到内存中并被操作系统调用,才拥有其生命期。进程(有时称为重量级进程)则是一个执行中 的程序。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。 操作系统管理其上所有进程的执行,并为这些进程合理地分配时间。进程也可以通过派生 (fork 或 spawn)新的进程来执行其他任务,不过因为每个新进程也都拥有自己的内存和数据 栈等,所以只能采用进程间通信(IPC)的方式共享信息。
线程
线程(有时候称为轻量级进程)与进程类似,不过它们是在同一个进程下执行的,并 共享相同的上下文。可以将它们认为是在一个主进程或“主线程”中并行运行的一些“迷 你进程”。
线程包括开始、执行顺序和结束三部分。它有一个指令指针,用于记录当前运行的上下 文。当其他线程运行时,它可以被抢占(中断)和临时挂起(也称为睡眠)——这种做法叫 做让步(yielding)。 一个进程中的各个线程与主线程共享同一片数据空间,因此相比于独立的进程而言,线 程间的信息共享和通信更加容易。线程一般是以并发方式执行的,正是由于这种并行和数据 共享机制,使得多任务间的协作成为可能。当然,在单核 CPU 系统中,因为真正的并发是不 可能的,所以线程的执行实际上是这样规划的:每个线程运行一小会儿,然后让步给其他线 程(再次排队等待更多的 CPU 时间)。在整个进程的执行过程中,每个线程执行它自己特定 的任务,在必要时和其他线程进行结果通信。
当然,这种共享并不是没有风险的。如果两个或多个线程访问同一片数据,由于数据访 问顺序不同,可能导致结果不一致。这种情况通常称为竞态条件(race condition)。幸运的是, 大多数线程库都有一些同步原语,以允许线程管理器控制执行和访问。
另一个需要注意的问题是,线程无法给予公平的执行时间。这是因为一些函数会在完成 前保持阻塞状态,如果没有专门为多线程情况进行修改,会导致 CPU 的时间分配向这些贪婪 的函数倾斜。
Python 中使用线程
thread 模块 ( python2 - thread;python3 - _thread)
>>> import _thread
>>>
1、不使用线程情况
#!/usr/bin/env python
# -*-coding:utf-8-*-
from time import sleep,ctime
def loop0():
print('start loop 0 at:',ctime())
sleep(4)
print('loop 0 done at:',ctime())
def loop1():
print('start loop 1 at:',ctime())
sleep(2)
print('loop 1 done at:',ctime())
def main():
print('starting at:',ctime())
loop0()
loop1()
print('all DONE at:',ctime())
if __name__ == '__main__':
main()
结果:
starting at: Fri Jun 19 18:27:39 2020
start loop 0 at: Fri Jun 19 18:27:39 2020
loop 0 done at: Fri Jun 19 18:27:43 2020
start loop 1 at: Fri Jun 19 18:27:43 2020
loop 1 done at: Fri Jun 19 18:27:45 2020
all DONE at: Fri Jun 19 18:27:45 2020
Process finished with exit code 0
2、使用线程
- 简单版
#!/usr/bin/env python
# -*-coding:utf-8-*-
import _thread
from time import sleep,ctime
def loop0():
print('start loop 0 at:',ctime())
sleep(4)
print('loop 0 done at:',ctime())
def loop1():
print('start loop 1 at:',ctime())
sleep(2)
print('loop 1 done at:',ctime())
def main():
print('starting at:',ctime())
_thread.start_new_thread(loop0,())
_thread.start_new_thread(loop1,())
sleep(6)
print('all DONE at:',ctime())
if __name__ == '__main__':
main()
结果
starting at: Fri Jun 19 18:41:43 2020
start loop 0 at: Fri Jun 19 18:41:43 2020
start loop 1 at: Fri Jun 19 18:41:43 2020
loop 1 done at: Fri Jun 19 18:41:45 2020
loop 0 done at: Fri Jun 19 18:41:47 2020
all DONE at: Fri Jun 19 18:41:49 2020
Process finished with exit code 0
- 使用线程和锁
#!/usr/bin/env python
# -*-coding:utf-8-*-
import _thread
from time import sleep,ctime
loops = [4,2]
def loop(nloop,nsec,lock):
print('start loop {0} at: {1}'.format(nloop,ctime()))
sleep(nsec)
print('loop {0} done at: {1}'.format(nloop,ctime()))
lock.release()
def main():
print('starting at:',ctime())
locks = []
nloops = range(len(loops))
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()
结果
starting at: Tue Jul 7 15:07:45 2020
start loop 0 at: Tue Jul 7 15:07:45 2020
start loop 1 at: Tue Jul 7 15:07:45 2020
loop 1 done at: Tue Jul 7 15:07:47 2020
loop 0 done at: Tue Jul 7 15:07:49 2020
all DONE at: Tue Jul 7 15:07:49 2020
Process finished with exit code 0
3、Python 的 threading 模块
Python 提供了多个模块来支持多线程编程,包括 thread、threading 和 Queue 模块等。程序是可以使用 thread 和 threading 模块来创建与管理线程。thread 模块提供了基本的线程和锁定支持;而 threading 模块提供了更高级别、功能更全面的线程管理。使用 Queue 模块,用户可以创建一个队列数据结构,用于在多线程之间进行共享。
推荐使用更高级别的 threading 模块,而不使用 thread 模块有很多原因。threading 模 块更加先进,有更好的线程支持,并且 thread 模块中的一些属性会和 threading 模块有冲突。 另一个原因是低级别的 thread 模块拥有的同步原语很少(实际上只有一个),而 threading 模块则有很多。
避免使用 thread 模块的另一个原因是它对于进程何时退出没有控制。当主线程结束时,所有其他线程也都强制结束,不会发出警告或者进行适当的清理。如前所述,至少 threading 模块能确保重要的子线程在进程退出前结束。
我们只建议那些想访问线程的更底层级别的专家使用 thread 模块。为了强调这一点,在 Python3 中该模块被重命名为_thread(python2 中为 thread)。你创建的任何多线程应用都应该使用 threading 模块或其他更高级别的模块。
- 使用 threading,创建 Thread 的实例,传给它一个函数
#!/usr/bin/env python
# -*-coding:utf-8-*-
import threading
from time import sleep,ctime
loops = [4,2]
def loop(nloop,nsec):
print('start loop {0} at: {1}'.format(nloop,ctime()))
sleep(nsec)
print('loop {0} done at: {1}'.format(nloop,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()
结果
starting at: Fri Jun 19 18:32:19 2020
start loop 0 at: Fri Jun 19 18:32:19 2020
start loop 1 at: Fri Jun 19 18:32:19 2020
loop 1 done at: Fri Jun 19 18:32:21 2020
loop 0 done at: Fri Jun 19 18:32:23 2020
all DONE at: Fri Jun 19 18:32:23 2020
Process finished with exit code 0
- 创建 Thred 的实例,传给它一个可调用的类实例
#!/usr/bin/env python
# -*-coding:utf-8-*-
import threading
from time import sleep,ctime
loops = [4,2]
class ThreadFunc(object):
def __init__(self,func,args,name=''):
self.func = func
self.args = args
self.name = name
def __call__(self):
self.func(*self.args)
def loop(nloop,nsec):
print('start loop {0} at: {1}'.format(nloop,ctime()))
sleep(nsec)
print('loop {0} done at: {1}'.format(nloop,ctime()))
def main():
print('starting at:',ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = threading.Thread(target=ThreadFunc(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()
结果与上一个一致
- 派生 Thread 的子类,并创建子类的实例
#!/usr/bin/env python
# -*-coding:utf-8-*-
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.func = func
self.args = args
self.name = name
def run(self):
print('start {0} {1}'.format(self.name,self.args[0]))
self.func(*self.args)
def loop(nloop,nsec):
print('start loop {0} at: {1}'.format(nloop,ctime()))
sleep(nsec)
print('loop {0} done at: {1}'.format(nloop,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()
结果
starting at: Fri Jun 19 18:36:44 2020
start loop 0
start loop 0 at: Fri Jun 19 18:36:44 2020
start loop 1
start loop 1 at: Fri Jun 19 18:36:44 2020
loop 1 done at: Fri Jun 19 18:36:46 2020
loop 0 done at: Fri Jun 19 18:36:48 2020
all DONE at: Fri Jun 19 18:36:48 2020
Process finished with exit code 0