Java转Python之并发
来源:互联网 发布:景泰蓝 珐琅 区别 知乎 编辑:程序博客网 时间:2024/06/06 01:04
标题没有使用Java常用的名词“多线程”,是因为Python的并发分为多进程和多线程,进程在multiprocessing模块,线程在threading模块(线程虽然还有_thread模块,但是threading是对_thread的高级封装,使用起来更顺手所以这里只介绍threading)
多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
先来看Python的多进程代码:
from multiprocessing import Processimport os# 子进程要执行的代码def run_proc(name) : print('Run child process %s (%s)...' % (name, os.getpid()))if __name__=='__main__': print('Parent process %s.' % os.getpid()) #入参要是元祖,进程内不允许修改 p1 = Process(target=run_proc, args=('test-1',)) p2 = Process(target=run_proc, args=('test-2',)) print('Child process will start.') p1.start() p2.start() p1.join() p2.join() print('Child process end.')
运行结果:
Parent process 4956.Child process will start.Run child process test-1 (11108)...Run child process test-2 (11172)...Child process end.
共有3个进程,主进程和2个子进程,参要是元祖,进程内不允许修改,Java多线程中也会将入参设置成final类型。
还可以用进程池来管理:
from multiprocessing import Poolimport os, time, randomdef myTask(name): print('Run task %s (%s)...' % (name, os.getpid())) start = time.time() time.sleep(random.random()) end = time.time() print('Task %s runs %0.2f seconds.' % (name, (end - start)))if __name__=='__main__': print('Parent process %s.' % os.getpid()) p = Pool(2) #进程数故意比池多 for i in range(5): p.apply_async(myTask, args=(i,)) print('Waiting for all subprocesses done...') #join之前必须closs,禁止进程池再接收新任务 p.close() #等待所有进程执行完毕 p.join() print('All subprocesses done.')
运行结果:
Parent process 15160.Waiting for all subprocesses done...Run task 0 (12588)...Run task 1 (7948)...Task 1 runs 0.37 seconds.Run task 2 (7948)...Task 0 runs 0.73 seconds.Run task 3 (12588)...Task 2 runs 0.48 seconds.Run task 4 (7948)...Task 3 runs 0.58 seconds.Task 4 runs 0.54 seconds.All subprocesses done.
进程池的方式可以控制运行中的进程数量,通过观察运行结果中进程号看得出进程池中只有两个进程。
进程间如何通信?使用Queue!现在有3个进程,2个负责写1个负责读:
from multiprocessing import Process, Queueimport os, time, random# 写数据进程执行的代码:def write(q): print('Process to write: %s' % os.getpid()) for element in ['A', 'B', 'C','D','E','F','G','H']: print('Write %s' %element) q.put(element) time.sleep(random.random())# 读数据进程执行的代码:def read(q): while True: #queue是阻塞队列,省去很多麻烦 element = q.get(True) print('Read %s' %element)if __name__=='__main__': # 父进程创建Queue,并传给各个子进程: q = Queue() pw1 = Process(target=write, args=(q,)) pw2 = Process(target=write, args=(q,)) pr = Process(target=read, args=(q,)) # 启动子进程pw,写入: pw1.start() pw2.start() # 启动子进程pr,读取: pr.start() # 等待pw结束: pw1.join() pw2.join() # pr进程里是死循环,无法等待其结束,只能强行终止: pr.terminate()
看完多进程我们再来看python多线程的开发,回到了Java人员熟悉的多线程算是如鱼得水,编码思路跟多进程基本一样,不同点是考虑到线程安全问题需要引入锁和ThreadLocal的概念。
Python锁的两个核心方法是:加锁lock.acquire()和释放锁lock.release(),为了保证锁一定要被释放我们往往将release()放在finally里
例子:import threadingimport timelock = threading.Lock()class 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): # 获得锁,成功获得锁定后返回True # 可选的timeout参数不填时将一直阻塞直到获得锁定 # 否则超时后将返回False while self.counter>0 : lock.acquire() #打印机打印3次 print_time(self.name, 3) # 释放锁 lock.release() self.counter-=1def print_time(threadName, counter): while counter: time.sleep(1) print("%s: %s" % (threadName, time.ctime(time.time()))) counter -= 1# 创建新线程thread1 = myThread(1, "Thread-1", 3)thread2 = myThread(2, "Thread-2", 3)# 开启新线程thread1.start()thread2.start()#等待线程执行完毕thread1.join()thread2.join()
执行结果:
Thread-1: Thu Dec 21 15:34:02 2017Thread-1: Thu Dec 21 15:34:03 2017Thread-1: Thu Dec 21 15:34:04 2017Thread-1: Thu Dec 21 15:34:05 2017Thread-1: Thu Dec 21 15:34:06 2017Thread-1: Thu Dec 21 15:34:07 2017Thread-2: Thu Dec 21 15:34:08 2017Thread-2: Thu Dec 21 15:34:09 2017Thread-2: Thu Dec 21 15:34:10 2017Thread-1: Thu Dec 21 15:34:12 2017Thread-1: Thu Dec 21 15:34:13 2017Thread-1: Thu Dec 21 15:34:14 2017Thread-2: Thu Dec 21 15:34:15 2017Thread-2: Thu Dec 21 15:34:16 2017Thread-2: Thu Dec 21 15:34:17 2017Thread-2: Thu Dec 21 15:34:18 2017Thread-2: Thu Dec 21 15:34:19 2017Thread-2: Thu Dec 21 15:34:20 2017
锁虽然能解决线程间资源共享的问题,但是必然会带来竞争,而且处理不好容易产生死锁。
为了线程安全Python也有Threadlocal的解决思路,跟前面介绍的一期Java中原理一样<ThreadLocal-单例模式下高并发线程安全>。代码:
import threading,time,random# 创建全局ThreadLocal对象:local = threading.local()class 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): # 获得锁,成功获得锁定后返回True # 可选的timeout参数不填时将一直阻塞直到获得锁定 # 否则超时后将返回False while self.counter>0 : local.name = self.name local.count = 3 #打印机打印3次 print_time() self.counter-=1def print_time(): threadName = local.name counter = local.count while counter: time.sleep(random.random()) print("%s: %s " % (threadName, time.ctime(time.time()))) counter -= 1# 创建新线程thread1 = myThread(1, "Thread-1", 3)thread2 = myThread(2, "Thread-2", 3)# 开启新线程thread1.start()thread2.start()#等待线程执行完毕thread1.join()thread2.join()
那么问题来了,多进程好还是多线程好?
多进程和多线程的任务调度都是Master-Worker模式的,Workder真正负责运算,Master负责监督Workder和收集运算结果。在多进程中,主进程是Master,子进程是Worker;在多线程中主线程是Master,子线程是Worker。
稳定性方面多进程比多线程要好,因为进程是独立分开的,子进程出现问题不会影响到其它进程;而多线程是共享进程内存的,子线程出现问题可能会导致该进程内整所有子线程一起完蛋!
在开销上多进程要比多线程要大,多线程要轻量很多,但是无论是多线程还是多进程同时运行的数量都不能太多,CPU调度会出现竞争,用于任务切换的消耗将远远大于用于worker任务的消耗。
数据共享方面,进程使用各自独立的存储,数据只能同步和交互无法共享;多线程间共用进程的同一个存储,可以共享同一个资源对象(也就是说非线程安全带来的好处)。
对于Java语言我们没得选,并发只能用多线程。但是对于Python语言当我们有的选的时候,先看规模,规模都很大的情况下如果不需要考虑数据共享,尽量用多进程,因为在分布式、微服务流行的年代,通过添加PC的方式进程数的限制不再成为瓶颈,为何不用更稳定的方式呢;如果要考虑数据共享先分析通过数据同步方式能否低消耗的解决,能解决还是用多进程,代价很大就用多线程。当然了运算规模很小,需要快餐式消费,还是多线程开销更小。
- Java转Python之并发
- python并发之multiprocessing
- java并发编程:并发容器之CopyOnWriteArrayList(转)
- 【转】Java并发编程之ConcurrentHashMap
- Java并发编程之ConcurrentHashMap(转)
- python并发之concurrent.futures
- python并发之协程
- python并发之concurrent.futures
- Java并发之BlockingQueue
- 【Java】并发之Executor
- Java 并发之中断
- 【Java并发之】BlockingQueue
- java并发之ConcurrentHashMap
- Java并发之volatile
- java并发之synchronized
- java并发之Lock
- java并发之volatile
- java并发之ThreadLocal
- iOS开发之国际化
- liferay6.2使用CXF开发WbService接口
- 使用python抓取网站代码,并下载里面的url
- mybatis调用mysql存储过程返回结果集
- Hibernate缓存
- Java转Python之并发
- Tensorflow学习比较-多线程
- Android:控件布局(相对布局)RelativeLayout
- tomcat下各个文件作用
- RecyclerView瀑布流
- Leetcode141. Linked List Cycle
- 基于onvif协议的视频监控系统(一):需要理解的关键字
- ubuntu 默认防火墙安装、启用、查看状态
- (转载)Android酷炫实用的开源框架(UI框架)