进程与线程的定义
一句话来叙述
简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
对于进行与线程的区别,之前在整理并行的时候整理过一次,这里再稍微解释下,
线程是CPU调度的基本单位,进程是操作系统分配资源的单位
这句话可以了解到一个比较简单的区分,
- 进程的行使资源的范围是大于线程的,一个进程里面可以有多个线程,每个进程如独特的地址与内存资源,而线程一般都是局部变量,实现变量的临时创建于共享,
- 线程也是有优点的,线程的并存性能,决定了一个进程里面的多个线程可以实现并行的计算,进程在python中可以中fork来进行创建,一般而言一个进程里面的多个线程都是处理属于这一个进程的任务,而多个进程一般都是处理同一个应用的不同任务,比如如下图
最后可以举一个火车的例子,一辆火车有多个车厢,每个车厢里面有很多人,火车就是一个电脑,而车厢就是多个进程,每个车厢里面的人就是多个线程。
python 中的多进程与多线程
python中的多进程可以通过两个方法来实现,首先是python的OS原生的方法,fork()一个新的进程,但是这个在跨平台的时候可能会出现一定的问题,在这里,我推荐用一个多进程的包,那就是multiprocessing模块的Process类来创建子进程,例子如下所示
from multiprocessing import Process
from os import getpid
from random import randint
from time import time, sleep
def download_task(filename):
print('启动下载进程,进程号[%d].' % getpid())
print('开始下载%s...' % filename)
time_to_download = randint(5, 10)
sleep(time_to_download)
print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))
def main():
start = time()
p1 = Process(target=download_task, args=('Python从入门到住院.pdf', ))
p1.start()
p2 = Process(target=download_task, args=('Peking Hot.avi', ))
p2.start()
p1.join()
#join([timeout])可以参考Python文档说明。
#大概意思就是调用join函数会使得主调线程阻塞,直到被调用线程运行结束或超时。
#参数timeout是一个数值类型,用来表示超时时间,如果未提供该参数,
#那么主调线程将一直阻塞直到被调用线程结束。
p2.join()
end = time()
print('总共耗费了%.2f秒.' % (end - start))
if __name__ == '__main__':
main()
其中,通过process函数创建了两个不同的进程,两个进程同时运行,从而节省了时间
多线程
python中的多线程,是很好实现的,主要是通过进程threading包中的Tread类来实现对多个线程的创建,例子如下所示,其实跟多进程很相似,都可以创建一个继承于Thread的线程类然后运行,或者是直接调用Thread函数,跟Process的用法是一样的:
from random import randint
from threading import Thread
from time import time,sleep
class DownloadTask(Thread):
def __init__(self,filename):
super().__init__()
self._filename=filename
def run(self):
print('开始下载%s...'%self._filename)
time_to_download=randint(5,10)
sleep(time_to_download)
print('%s下载完成!消耗%d秒'%(self._filename,time_to_download))
def main():
start=time()
t1=DownloadTask('python 是智障')
t1.start()#start() 方法是启动一个子线程,这里做个小提示,注意区分start()跟run()的区别,run不会创建一个新的子进程,如果你直接run()的话,一般是直接运行main thread
t2=DownloadTask('gaygay 成是gay')
t2.start()
t1.join()
t2.join()
end=time()
print('总共消耗了%.2f秒.'%(end-start))
# print('总共耗费了%.2f秒.' % (end - start))
if __name__=='__main__':
main()
上面就是多个线程的实现。
临界资源加锁的实现
在多个进程同时运行的时候,变量的共享是一个问题,有的变量是局部变量,有的是全局变量,但是全局变量如果多个线程多是占用的时候,很容易发生变量错误的结果,这种的全局变量在一定程度上也是临界资源,比如多个人同时向一个用户的账号打钱,如果同时调用,那么最后的账户可能是远远小于预想的结果的。这里补充下临界资源的定义
多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用
这种资源就是临界资源
那么怎么处理临界资源呢,其实学过操作系统的同学可能知道锁的概念,以及如何加锁,以及死锁的情况和解决方法,在这里,python中是一样的道理,这里用多个用户向一个账户里打钱的例子来解释,主要是用到i了thread种的lack函数:
from time import sleep
from threading import Thread, Lock
class Account(object):
def __init__(self):
self._balance = 0
self._lock = Lock()
def deposit(self, money):
# 先获取锁才能执行后续的代码
self._lock.acquire()
try:
new_balance = self._balance + money
sleep(0.01)
self._balance = new_balance
finally:
# 在finally中执行释放锁的操作保证正常异常锁都能释放
self._lock.release()
@property
def balance(self):
return self._balance
class AddMoneyThread(Thread):
def __init__(self, account, money):
super().__init__()
self._account = account
self._money = money
def run(self):
self._account.deposit(self._money)
def main():
account = Account()
threads = []
for _ in range(100):
t = AddMoneyThread(account, 1)
threads.append(t)
t.start()
for t in threads:
t.join()
print('账户余额为: ¥%d元' % account.balance)
if __name__ == '__main__':
main()
但是Python的多线程并不能发挥CPU的多核特性,之所以如此,是因为Python的解释器有一个“全局解释器锁”(GIL)的东西,任何线程执行前必须先获得GIL锁,然后每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行,这个就不如c语言那样灵活了。所以在这个地方,友好的是可以用python调c中的并行计算MPI包来实现,个人觉得还是很好用的,具体如何调用,下次更新的时候进行详细的介绍。
但是python里面的多进程与多线程并不是没用用的,其实他在异步I/O的时候可以使用多个进程进程处理,而在实现分治法处理大容量数据的时候,还是可以用的。
本文中的例子来自于
Python - 100天从新手到大师
网友评论