Python中的进程,线程,协程

什么是进程:

     编写完毕的代码,在没有运⾏的时候,称之为程序,正在运⾏着的代码,就成为进程

    进程是一个动态的过程,占有cpu内存的计算机资源,有一定的生命周期

    进程是程序在计算机中一次执行的过程

创建子进程:

    第一种:fork(只在Unix/Linux/Mac上运⾏,windows不可以)

        程序执⾏到os.fork()时,操作系统会创建⼀个新的进程(⼦进程),然后复制⽗进程的所有信息到⼦进程中

        然后⽗进程和⼦进程都会从fork()函数中得到⼀个返回值,在⼦进程中这个值⼀定是0,⽽⽗进程中是⼦进程的 id号,⽽⼦进程只需要调⽤getppid()就可以拿到⽗进程的ID。

例子:

import os
rpid = os.fork()
if rpid<0:
    print("fork调⽤失败。")
elif rpid == 0:
    print("我是⼦进程(%s),我的⽗进程是(%s)"%(os.getpid(),os.getppid()))
else:
    print("我是⽗进程(%s),我的⼦进程是(%s)"%(os.getpid(),rpid))
print("⽗⼦进程都可以执⾏这⾥的代码")

#我是⼦进程(3252),我的⽗进程是(3251)
#我是⽗进程(3251),我的⼦进程是(3252)
#⽗⼦进程都可以执⾏这⾥的代码
#⽗⼦进程都可以执⾏这⾥的代码

注:⽗进程、⼦进程执⾏顺序没有规律,完全取决于操作系统的调度算法

    第二种:multiprocessing中的Process

例子:

from multiprocessing import Process
import os
# 子进程要执⾏的代码
def run_proc(name):
    print('子进程运⾏中,name= %s ,pid=%d...' % (name, os.getpid()))
if __name__=='__main__':
    print('父进程 %d.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('子进程将要执⾏')
    p.start()
    p.join()
    print('子进程已结束')

说明
创建⼦进程时,只需要传⼊⼀个执⾏函数和函数的参数,创建⼀个Process实例,⽤start()⽅法启动,这样创建进程⽐fork()还要简单。
join()⽅法可以等待⼦进程结束后再继续往下运⾏,通常⽤于进程间的同步。

Process语法结构如下:
    Process([group [, target [, name [, args [, kwargs]]]]])
    target:表示这个进程实例所调用对象;
    args:表示调用对象的位置参数元组;
    kwargs:表示调用对象的关键字参数字典;
    name:为当前进程实例的别名;
    group:大多数情况下用不到;
Process类常用法:
    is_alive():判断进程实例是否还在执行;
    join([timeout]):是否等待进程实例执行结束,或等待多少秒;
    start():启动进程实例(创建子进程);
    run():如果没有给定target参数,对这个对象调用start()方法时,就将执行对象中的run()方法;
terminate():不管任务是否完成,立⽴即终止;
Process类常用属性:
name:当前进程实例别名,默认为Process-N,N为从1开始递增的整
数;
pid:当前进程实例的PID值;

进程池的创建:

multiprocessing.Pool常⽤函数解析:
apply_async(func[, args[, kwds]]) :使⽤⾮阻塞⽅式调⽤func(并⾏执⾏,堵塞⽅式必须等待上⼀个进程退出才能执⾏下⼀个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
apply(func[, args[, kwds]]):使⽤阻塞⽅式调⽤func
close():关闭Pool,使其不再接受新的任务;
terminate():不管任务是否完成,⽴即终⽌;
join():主进程阻塞,等待⼦进程的退出, 必须在close或terminate之后使⽤;
 

进程间的通信

    可以使⽤multiprocessing模块的Queue实现多进程之间的数据传递

说明
初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最⼤可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头);
Queue.qsize():返回当前队列包含的消息数量;
Queue.empty():如果队列为空,返回True,反之False ;
Queue.full():如果队列满了,返回True,反之False;
Queue.get([block[, timeout]]):获取队列中的⼀条消息,然后将其从列队中移除,block默认值为True;
        如果block使⽤默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为⽌,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常;

       如果block值为False,消息列队如果为空,则会⽴刻抛出"Queue.Empty"异常;
           Queue.get_nowait():相当Queue.get(False);
           Queue.put(item,[block[, timeout]]):将item消息写⼊队列,block默认值为True;
       如果block使⽤默认值,且没有设置timeout(单位秒),消息列队如果已经没有空间可写⼊,此时程序将被阻塞(停在写⼊状态),直到从消息列队腾出空间为⽌,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常;
        如果block值为False,消息列队如果没有空间可写⼊,则会⽴刻抛出"Queue.Full"异常;
            Queue.put_nowait(item):相当Queue.put(item, False);

注意进程池中的Queue
如果要使⽤Pool创建进程,就需要使⽤multiprocessing.Manager()中的Queue(),⽽不是multiprocessing.Queue(),否则会得到⼀条如下的错误信息:
RuntimeError: Queue objects should only be shared between processes
through inheritance.

多线程-threading模块里的Thread

在多线程开发中,全局变量是多个线程都共享的数据,⽽局部变量等是各⾃线程的,是⾮共享的

#I'm Thread-1 @ 0
#I'm Thread-1 @ 1
#I'm Thread-1 @ 2
进程与线程的区别
⼀个程序⾄少有⼀个进程,⼀个进程⾄少有⼀个线程.
线程的划分尺度⼩于进程(资源⽐进程少),使得多线程程序的并发性⾼。
进程在执⾏过程中拥有独⽴的内存单元,⽽多个线程共享内存,从⽽极⼤地提⾼了程序的运⾏效率
线线程不能够独⽴执⾏,必须依存在进程中

优缺点
线程和进程在使⽤上各有优缺点:线程执⾏开销⼩,但不利于资源的管理和保护;⽽进程正相反。

threading模块中定义了Lock类,可以⽅便的处理锁定:
#创建锁
mutex = threading.Lock()

#锁定
mutex.acquire([blocking])
#释放
mutex.release()

例子:

from threading import Thread, Lock
import time
g_num = 0
def test1():
    global g_num
    for i in range(1000000):
#True表示堵塞 即如果这个锁在上锁之前已经被上锁了,那么这个线程会在这⾥⼀直等待到解锁为⽌
#False表示⾮堵塞,即不管本次调⽤能够成功上锁,都不会卡在这,⽽是继续执⾏下⾯的代码
        mutexFlag = mutex.acquire(True)
        if mutexFlag:
            g_num += 1
            mutex.release()
    print("---test1---g_num=%d"%g_num)
def test2():
    global g_num
    for i in range(1000000):
        mutexFlag = mutex.acquire(True) #True表示堵塞
        if mutexFlag:
            g_num += 1
            mutex.release()
    print("---test2---g_num=%d"%g_num)

#创建⼀个互斥锁
#这个所默认是未上锁的状态
mutex = Lock()
p1 = Thread(target=test1)
p1.start()
p2 = Thread(target=test2)
p2.start()
print("---g_num=%d---"%g_num)

锁的好处:
确保了某段关键代码只能由⼀个线程从头到尾完整地执⾏
锁的坏处:
阻⽌了多线程并发执⾏,包含锁的某段代码实际上只能以单线程模式执⾏,效率就⼤⼤地下降了
由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对⽅持有的锁时,可能会造成死锁

例子:

import threading
import time
class MyThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm "+self.name+' @ '+str(i) #name属性中保存的是当前线程的名字
            print(msg)
if __name__ == '__main__':
    t = MyThread()
    t.start()

 

例子:

from threading import Thread,Lock
from time import sleep
class Task1(Thread):
    def run(self):
        while True:
            if lock1.acquire():
                print("------Task 1 -----")
                sleep(0.5)
                lock2.release()
class Task2(Thread):
    def run(self):
        while True:
            if lock2.acquire():
                print("------Task 2 -----")
                sleep(0.5)
                lock3.release()
class Task3(Thread):
    def run(self):
        while True:
            if lock3.acquire():
                print("------Task 3 -----")
                sleep(0.5)
                lock1.release()
#使⽤Lock创建出的锁默认没有“锁上”
lock1 = Lock()
#创建另外⼀把锁,并且“锁上”
lock2 = Lock()
lock2.acquire()
#创建另外⼀把锁,并且“锁上”
lock3 = Lock()
lock3.acquire()
t1 = Task1()
t2 = Task2()
t3 = Task3()
t1.start()
t2.start()
t3.start()

#------Task 1 -----
#------Task 2 -----
#------Task 3 -----
#------Task 1 -----
#------Task 2 -----
#------Task 3 -----

例子:

#encoding=utf-8
import threading
import time
#python2中
# from Queue import Queue
#python3中
from queue import Queue
class Producer(threading.Thread):
    def run(self):
        global queue
        count = 0
        while True:
            if queue.qsize() < 1000:
                for i in range(100):
                    count = count +1
                    msg = '⽣成产品'+str(count)
                    queue.put(msg)
                    print(msg)
            time.sleep(0.5)
class Consumer(threading.Thread):
    def run(self):
        global queue
        while True:
            if queue.qsize() > 100:
                for i in range(3):
                    msg = self.name + '消费了 '+queue.get()
                    print(msg)
            time.sleep(1)
if __name__ == '__main__':
    queue = Queue()
    for i in range(500):
        queue.put('初始产品'+str(i))
    for i in range(2):
        p = Producer()
        p.start()
    for i in range(5):
        c = Consumer()
        c.start()

例子:

import threading
# 创建全局ThreadLocal对象:
local_school = threading.local()
def process_student():
# 获取当前线程关联的student:
    std = local_school.student
    print('Hello, %s (in %s)' % (std, threading.current_thread().name))
def process_thread(name):
# 绑定ThreadLocal的student:
    local_school.student = name
    process_student()
t1 = threading.Thread(target= process_thread, args=('dongGe',))
t2 = threading.Thread(target= process_thread, args=('⽼王',))
t1.start()
t2.start()
t1.join()
t2.join()

对于计算密集型用多进程,多IO密集型用多线程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值