一、多进程

#一个程序运行起来之后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单位,不仅可以通过线程完成多任务,进程也是可以的
#进程之间是相互独立的
#cpu密集的时候适合用多进程




python 进程 线程 通信 python进程线程区别_多线程

python 进程 线程 通信 python进程线程区别_多线程_02

#进程之间不共享
import multiprocessing
from multiprocessing import Pool
import time
import threading
g_num = 0
def edit():
    global g_num
    for i in range(10):
        g_num += 1

def reader():
    print(g_num)


if __name__ == '__main__':
    p1 = multiprocessing.Process(target=edit)
    p2 = multiprocessing.Process(target=reader())
    p1.start()
    p2.start()
    p1.join()
    p2.join()

进程之间资源不共享


进程之间不共享



python 进程 线程 通信 python进程线程区别_多线程

python 进程 线程 通信 python进程线程区别_多线程_02

#多进程并发
import multiprocessing
from multiprocessing import Pool
import time
def test1():
    for i in range(10):
        time.sleep(1)
        print('test', i)

def test2():
    for i in range(10):
        time.sleep(1)
        print('test', i)

if __name__ == '__main__':
    p1 = multiprocessing.Process(target=test1)
    p2 = multiprocessing.Process(target=test2)
    p1.start()
    p2.start()

多进程并发


多进程并发



python 进程 线程 通信 python进程线程区别_多线程

python 进程 线程 通信 python进程线程区别_多线程_02

#进程池
import multiprocessing
from multiprocessing import Pool
import time
import threading
g_num = 0
def test1(n):
    for i in range(n):
        time.sleep(1)
        print('test1', i)

def test2(n):
    for i in range(n):
        time.sleep(1)
        print('test2', i)
def test3(n):
    for i in range(n):
        time.sleep(1)
        print('test3', i)

def test4(n):
    for i in range(n):
        time.sleep(1)
        print('test4', i)

if __name__ == '__main__':
    pool = Pool(3)#把进程声明出来括号里不写东西说明无限制,如果写数字,就是最大的进程数
    pool.apply_async(test1,(10,))#用pool去调用函数test1,参数为10格式为(10,)
    pool.apply_async(test2,(10,))#用pool去调用函数test2,参数为10格式为(10,)
    pool.apply_async(test3,(10,))#用pool去调用函数test3,参数为10格式为(10,)
    pool.apply_async(test4,(10,))#用pool去调用函数test4,参数为10格式为(10,)
    pool.close()#close必须在join的前面
    pool.join()

进程池并发


进程池并发


二、多线程

多线程特点:

#线程的并发是利用cpu上下文的切换(是并发,不是并行)
#多线程执行的顺序是无序的
#多线程共享全局变量
#线程是继承在进程里的,没有进程就没有线程
#GIL全局解释器锁
#只要在进行耗时的IO操作的时候,能释放GIL,所以只要在IO密集型的代码里,用多线程就很合适



#线程的并发是无序的
def test1(n):
    time.sleep(1)
    print('task', n)


for i in range(10):
    t = threading.Thread(target=test1,args=('t-%s' % i,))
    t.start()



#计算并发所用的时间
import threading
import time

def test1(n):
    time.sleep(1)
    print('task', n)

def test2(n):
    time.sleep(1)
    print('task', n)

start = time.time()
l = []
t1 = threading.Thread(target=test1, args=(1,))
t2 = threading.Thread(target=test1, args=(2,))
t1.start()
t2.start()
l.append(t1)
l.append(t2)
for i in l:
    i.join()
end = time.time()
print(end - start)



三、GIL全局解释器锁
GIL全称是

:Global Interpreter Lock,意思就是全局解释器锁,这个GIL并不是python的特性,他是只在Cpython解释器里引入的一个概念,而在其他的语言编写的解释器里就没有这个GIL例如:Jython,Pypy




为什么会有gil?:




       随着电脑多核cpu的出现核cpu频率的提升,为了充分利用多核处理器,进行多线程的编程方式更为普及,随之而来的困难是线程之间数据的一致性和状态同步,而python也利用了多核,所以也逃不开这个困难,为了解决这个数据不能同步的问题,设计了gil全局解释器锁。




说到gil解释器锁,我们容易想到在多线程中共享全局变量的时候会有线程对全局变量进行的资源竞争,会对全局变量的修改产生不是我们想要的结果,而那个时候我们用到的是python中线程模块里面的互斥锁,哪样的话每次对全局变量进行操作的时候,只有一个线程能够拿到这个全局变量;看下面的代码:




python 进程 线程 通信 python进程线程区别_多线程

python 进程 线程 通信 python进程线程区别_多线程_02

import threading
global_num = 0


def test1():
    global global_num
    for i in range(1000000):
        global_num += 1

    print("test1", global_num)


def test2():
    global global_num
    for i in range(1000000):
        global_num += 1

    print("test2", global_num)

t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
在上面的例子里,我们创建了两个线程来争夺对global_num的加一操作,但是结果并非我们想要的,所以我们在这里加入了互斥锁

import threading
import time
global_num = 0

lock = threading.Lock()

def test1():
    global global_num
    lock.acquire()
    for i in range(1000000):
        global_num += 1
    lock.release()
    print("test1", global_num)


def test2():
    global global_num
    lock.acquire()
    for i in range(1000000):
        global_num += 1
    lock.release()
    print("test2", global_num)

t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
start_time = time.time()

t1.start()
t2.start()


View Code

在高并发期间,不是每一个线程都可以完成自身所有的任务的,这就导致线程任务没有完成,进程就结束了,此时就需要GIL,但是使用了GIL就从多线程变成了单线程。

python 进程 线程 通信 python进程线程区别_多线程_09

四、协程并发(gevent)

# 进程是资源分配的单位
# 线程是操作系统调度的单位
# 进程切换需要的资源最大,效率低
# 线程切换需要的资源一般,效率一般
# 协程切换任务资源很小,效率高
# 多进程、多线程根据cpu核数不一样可能是并行的,但是协成在一个线程中



#协程,自动切换
import gevent,time
from gevent import monkey
monkey.patch_all()
def test1():
    for i in range(10):
        time.sleep(1)
        print('test1', 1)

def test2():
    for i in range(10):
        time.sleep(2)
        print('test2', 1)

g1 = gevent.spawn(test1)
g2 = gevent.spawn(test2)
g1.join()
g2.join()

协程



#server端
import gevent
from gevent import socket, monkey
monkey.patch_all()
def server_recv_conn(port):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(200)
    while True:
        conn, addr = s.accept()
        g = gevent.spawn(recv_request, conn)
        g.join()
def recv_request(conn):
    while True:
        data = conn.recv(1024)
        data = data.decode('utf-8')
        print("recv:", data)
        conn.send(data.upper().encode('utf-8'))

if __name__ == '__main__':
    server_recv_conn(8888)







#client端
import socket

HOST = 'localhost'
PORT = 8888
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    msg = input("==>:")
    s.send(msg.encode('utf-8'))
    data = s.recv(1024)
    print('接收:', data.decode('utf-8'))
s.close()

协程实现socket server高并发