python之进程与线程

一、多进程

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

进程之间资源不共享
进程之间不共享
#多进程并发
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()

多进程并发
多进程并发
#进程池
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中线程模块里面的互斥锁,哪样的话每次对全局变量进行操作的时候,只有一个线程能够拿到这个全局变量;看下面的代码:

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之进程与线程

四、协程并发(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高并发
 

相关推荐