python实现多任务有三种方式:进程、线程、协程。
进程
fork
#encoding=utf-8
import os
import time
pid = os.fork()
if pid == 0: #新进程、子进程
while True:
print('唱歌')
time.sleep(1)
else: #主进程、父进程
while True:
print('跳舞')
time.sleep(1)
#运行结果是同时产生
唱歌
跳舞
...
getpid(),getppid()
- 主进程中
os.getpid()
和子进程中调用os.getppid()
会得到同一个值。一般子进程的os.getpid()
比父进程的大,自增产生。 pid = os.fork()
在父进程中返回的值是子进程的id值,在子进程中返回0。- 父子进程谁先执行不确定。
- 主进程结束,子进程不会结束,直到子进程自己结束
多进程中的全局变量: 多进程中,每个进程中所有数据(包括全局变量)都各自拥有一份,互不影响
多次fork
在某个进程里调用fork,则只会多出一个进程。
import os
pid = os.fork()
if pid == 0:
print('child process %d'%(os.getpid()))
else:
print('parent process %d'%(os.getpid()))
pid = os.fork()
if pid == 0:
print('child process 2 %d'%(os.getpid()))
else:
print('parent process 2 %d'%(os.getpid())
#打印结果:
parent process 39763
parent process 2 39763
child process 39800
child process 2 39801
fork炸弹
while True:
fork()
multiprocessing模块
fork
在Windows平台不可用,multiprocessing
模块是Python提供的跨平台的的多进程的支持。
from multiprocessing import Process
def test():
print('')
p = Process(target=test)
p.start() #让这个进程执行test函数的代码
Process创建子进程,主进程会等待所有的子进程结束后自己才结束,和fork()是不一样的。
join子进程
from multiprocessing import Process
def test():
print('------test-------')
p = Process(target=test)
p.start()
p.join([timeout]) #等待子进程结束[多少秒]后继续往下走
print('------main-------')
常用Process方法:
start()
, join()
, terminate()
Process子类
另外一个创建子进程的方法:
class ChildClass(Process):
def __init__(self):
pass
#start()后会自动调用run方法
def run(self):
pass
if __name__ == '__main__':
cc = ChiildClass()
cc.start()
cc.join()
进程池Pool
又是另一种创建子进程的方式。
#coding=utf-8
from multiprocessing import Pool
import os
def work(n):
print('----work--->%d-----%d-------'%(n,os.getpid()))
po = Pool(3)
for i in range(10):
#po.apply(work,(i,)) #以堵塞方式添加任务。几乎不用
po.apply_async(work,(i,))
po.close() #关闭Pool,使之不再接受新的任务
po.join() #必须等待所有子进程执行完成才结束,必须放在close方法后。否则,主进程马上就结束了
执行结果
----work--->0-----43452-------
----work--->1-----43452-------
----work--->2-----43452-------
----work--->3-----43452-------
----work--->5-----43452-------
----work--->6-----43452-------
----work--->7-----43452-------
----work--->8-----43452-------
----work--->9-----43452-------
----work--->4-----43453-------
进程间通信-Queue
用Queue可以实现进程间的数据共享
#coding=utf-8
from multiprocessing import Queue
q = Queue(3)
print(q.empty()) #True
q.put('123') #使用之前判断是否full
q.put('345')
q.put('222')
print(q.full()) #False
print(q.get()) #使用之前使用empty判断
print(q.full()) #True
q.get()相当于q.get(True) 阻塞读取
q.get_nowait相当于 q.get(False)
put类似
进程池之间的Queue
#coding=utf-8
from multiprocessing import Manager,Pool
import os
def reader(q):
print('reader启动(%s),父进程-%s'%(os.getpid(),os.getppid()))
for i in range(q.qsize()):
print('reader从Queue获取消息:%s'%q.get(True))
def writer(q):
print('writer启动(%s),父进程-%s'%(os.getpid(),os.getppid()))
for i in 'bendeng':
q.put(i)
if __name__ == "__main__":
print('进程-%s start'%os.getpid())
q = Manager().Queue()
po = Pool()
po.apply(writer,(q,))
po.apply(reader,(q,))
po.close()
po.join()
print('进程-%s End'%os.getpid())
线程
#coding=utf-8
from threading import Thread
def test():
print('test')
for i in range(5):
t = Thread(target=test)
print(t.getName())
t.start()
#执行结果
Thread-1
test
Thread-2
Thread-3
test
test
Thread-4
test
Thread-5
test
- 和进程类似,也可以使用Thread子类实现多线程
#coding=utf-8
import threading
class MyThread(threading.Thread):
def run(self):
for i in range(5):
print(self.name)
if __name__ == "__main__":
t = MyThread()
t.start()
主线程会等待子线程结束后才结束。多线程执行是无序的。
PS:0号进程负责切换进程,1号进程负责生成进程。孤儿进程也是被1号进程收容。
- 多线程中的全局变量
全局变量在多线程中是共享的。但全局变量在多线程中有可能是不准确的。于是产生了同步的问题。
同步-互斥锁
使用Lock来处理多线程处理全局变量的问题
#coding=utf-8
from threading import Thread,Lock
g_n = 0
def test1():
global g_n
#print(threading.current_thread())
mutex.acquire() #上锁
for i in range(1000000):
g_n += 1
mutex.release() #解锁
print('test1_g_n=%d'%(g_n))
def test2():
global g_n
#print(threading.current_thread())
mutex.acquire() #上锁
for i in range(1000000):
g_n += 1
mutex.release() #解锁
print('test2_g_n=%d'%(g_n))
#创建一个互斥锁,默认是不上锁的
mutex = Lock()
t1 = Thread(target=test1)
t1.start()
t2 = Thread(target=test2)
t2.start()
print('g_n=%d'%(g_n))
两个线程执行同一个函数,函数里面的变量(非共享数据)也是独立的,互不影响。
死锁
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
避免死锁的方法:
- 程序设计时要尽量避免(银行家算法)
- 添加超时时间
同步
同步就是协同步调,按预定的先后次序进行运行。
lock2.acquire -> lock1.release
lock1.acquire -> lock3.release
lock3.acquire -> end
生产者与消费者模式
阻塞队列是用来生产者和消费者来解耦的。
ThreadLocal
多线程中处理局部变量的问题。
#coding=utf-8
import threading
thread_local = threading.local()
def process():
obj = thread_local.obj
print('handle %s (in %s)'%(obj,threading.currentThread()))
def process_thread(name):
thread_local.obj = name
process()
t1 = threading.Thread(target=process_thread,args=("ben",),name='Thread-A')
t2 = threading.Thread(target=process_thread,args=("deng",),name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
#执行结果
handle ben (in <Thread(Thread-A, started 123145532739584)>)
handle deng (in <Thread(Thread-B, started 123145536946176)>)
异步
子进程结束,主进程接着做下一个事情。
#coding=utf-8
from multiprocessing import Pool
import time,os
def test1():
print('进程中的进程---pid=%d,ppid=%d'%(os.getpid(),os.getppid()))
for i in range(3):
time.sleep(1)
return 'test1 return'
def test2(args):
print('callback------pid=%d'%(os.getpid()))
print('callback args=%s'%args)
po= Pool(3)
po.apply_async(func=test1,callback=test2)
po.close()
po.join()
time.sleep(3)
print('主进程id=%d'%(os.getpid()))
#执行结果:
进程中的进程---pid=68912,ppid=68911
callback------pid=68911
callback args=test1 return
主进程id=68911
GIL
GIL全名全局解释器锁。在Python中,多线程是假的,在多核CPU的情况下,多线程使用了一个叫GIL
的变量控制了一个线程只能占用一核CPU。
from threading import Thread
def deadloop():
while True:
pass
t = Thread(target = deadloop)#lib.DeadLoop
t.start()
#main thread also deadloop
while True:
pass
结果双核CPU使用率并没有到100%。使用htop
命令查看。
所以在Python中,多进程比多线程效率高[多核]。
解决方案:
1、能用线程用进程
2、使用C语言解决。
from ctypes import *
from threading import Thread
#load c lib
lib = cdll.LoadLibrary("./libdeadloop.so")
t = Thread(target = lib.DeadLoop)
t.start()
#main thread also deadloop
#lib.DeadLoop()
while True:
pass
C语言代码如下:
//一个死循环
void DeadLoop(){
while(1){
;
}
}
在Linux系统中使用命令gcc xxx.c -shared -o libxxxx.so
将c文件编译成一个动态库文件。
然后运行情况如下:
双核CPU的使用率都达到了100%!
从Ubuntu安装Sublime Text3
1、添加Sublime-text-3软件包的软件源
sudo add-apt-repository ppa:webupd8team/sublime-text-3
2、使用以下命令更新系统软件源
sudo apt-get update
3、使用以下命令安装Sublime-text-3
sudo apt-get install sublime-text-installer
4、最后可在Dash Home中见到Sublime-text的软件图标,点击就可使用了.
- 从命令行启动
subl