python线程进程笔记



调度算法
时间片轮流
优先级调度


进程:
1、导入os模块
2、ret=os.fork():创建一个子进程
3、分为两种情况:ret==0:
ret!=0:
例:
import os


    # 注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以,我们不用folk()了解即可
    pid = os.fork()
print(pid)//在父进程打印的是子进程的id
    if pid == 0:
        print('--子进程--%d---'%os.getpid())
  elif pid>0:
        print('--父进程--%d-%d-'%(os.getpid(),os.getppid()))
else:
print('调用失败')


print("父子进程都执行的代码")
结果:
20570
--父进程--20569--
0
---子进程---20570-20569-


普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,
因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。
子进程永远返回0,而父进程返回子进程的ID。
这样做的理由是,一个父进程可以fork出很多子进程,
所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。


注意:
1、父子进程的先后顺序,父进程不会因为子进程没有结束,而不结束


2、多进程中,每个进程中所有数据(包括全局变量)都各有拥有一份,互不影响

3、父进程、子进程执行顺序没有规律,完全取决于操作系统的调度算法

4、fork函数,只在Unix/Linux/Mac上运行,windows不可以,以后不用fock()


multiprocessing(重点):linux和windows都可以执行
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()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。


注意:
1、主进程会等待子进程完成后,在结束进程


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值;




进程池Pool:
from multiprocessing import Pool  #第一步导入Pool方法
import os,time,random


def worker(msg):
    t_start = time.time()
    print("%s开始执行,进程号为%d"%(msg,os.getpid()))
    #random.random()随机生成0~1之间的浮点数
    time.sleep(random.random()*2) 
    t_stop = time.time()
    print(msg,"执行完毕,耗时%0.2f"%(t_stop-t_start))


po=Pool(3) #定义一个进程池,最大进程数3
for i in range(0,10):
    #Pool.apply_async(要调用的目标,(传递给目标的参数元祖,))
    #每次循环将会用空闲出来的子进程去调用目标
    po.apply_async(worker,(i,))  #注意若元组只有一个值时,要加个逗号


print("----start----")
po.close() #关闭进程池,关闭后po不再接收新的请求
po.join() #,必须放在close语句之后,默认主进程完成后结束,但执行该方法后,会等待po中所有子进程执行完成
print("-----end-----")




多个进程创建比较:
一般使用进程池,不使用folk()






进程间通信-Queue:(重点)


可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序
    Queue.qsize():返回当前队列包含的消息数量;
    Queue.empty():如果队列为空,返回True,反之False ;
    Queue.full():如果队列满了,返回True,反之False;
    Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True,
消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止,
如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常;
    Queue.get_nowait():相当Queue.get(False);
    Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True;
    Queue.put_nowait(item):相当Queue.put(item, False);






例:
from multiprocessing import Process, Queue
import os, time, random


# 写数据进程执行的代码:
def write(q):
    for value in ['A', 'B', 'C']:
        print 'Put %s to queue...' % value
        q.put(value)
        time.sleep(random.random())


# 读数据进程执行的代码:
def read(q):
    while True:
        if not q.empty():
            value = q.get(True)
            print 'Get %s from queue.' % value
            time.sleep(random.random())
        else:
            break


if __name__=='__main__':
    # 父进程创建Queue,并传给各个子进程:
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    # 启动子进程pw,写入:
    pw.start()    
    # 等待pw结束:
    pw.join()
    # 启动子进程pr,读取:
    pr.start()
    pr.join()
    # pr进程里是死循环,无法等待其结束,只能强行终止:
    print ''
    print '所有数据都写入并且读完'




如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),
即q=Manager.Queue()来创建队列






多线程-threading
1、使用threading模块
from threading import Thread
t=Thread(target=方法)
t.start()#启动线程
2. 主线程会等待所有的子线程结束后才结束
3、查看线程数量:length = len(threading.enumerate())




4、使用threading模块时,往往会定义一个新的子类class,只要继承threading.Thread就可以了,然后重写run方法


5、多线程程序的执行顺序是不确定的。当执行到sleep语句时,线程将被阻塞(Blocked),
   到sleep结束后,线程进入就绪(Runnable)状态,等待调度。而线程调度将自行选择一个线程执行。




6、当线程的run()方法结束时该线程完成。




7、在一个进程内的所有线程共享全局变量,能够在不适用其他方式的前提下完成多线程之间的数据共享(这点要比多进程要好)
   缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)


8、列表当做实参传递到线程中----------->t2 = Thread(target=work2, args=(g_nums,))


9、在多线程开发中,全局变量是多个线程都共享的数据,而局部变量等是各自线程的,是非共享的


进程与线程区别:

进程是系统进行资源分配和调度的一个独立单位.


线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位
线程同步:


同步就是协同步调,按预定的先后次序进行运行

使用互斥锁来实现


互斥锁:
当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。


理解:
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;
直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。
互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。


#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire([blocking])--->blocking为True,则当前线程会堵塞,直到获取到这个锁为止,为False,则当前线程不会堵塞,默认True
返回类型:True/False
#释放
mutex.release()


ThreadLocal

用threading.local()创建一个全局变量


一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。

ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题


例:
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',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('老王',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()


执行结果:


Hello, dongGe (in Thread-A)
Hello, 老王 (in Thread-B)






异步(重点)


同步调用就是你 喊 你朋友吃饭 ,你朋友在忙 ,你就一直在那等,等你朋友忙完了 ,你们一起去
异步调用就是你 喊 你朋友吃饭 ,你朋友说知道了 ,待会忙完去找你 ,你就去做别的了。


例:
from multiprocessing import Pool
import time
import os


def test():
    print("---进程池中的进程---pid=%d,ppid=%d--"%(os.getpid(),os.getppid()))
    for i in range(3):
        print("----%d---"%i)
        time.sleep(1)
    return "hahah"


def test2(args):
    print("---callback func--pid=%d"%os.getpid())
    print("---callback func--args=%s"%args)


pool = Pool(3)
pool.apply_async(func=test,callback=test2) //callback为回调函数,当子进程完成后,系统告诉主进程去做回调函数的事


while True:
sleep(1)
print("----主进程-pid=%d----"%os.getpid())


运行结果:


---进程池中的进程---pid=9401,ppid=9400--
----0---
----1---
----2---
----主进程-pid=9400----
----主进程-pid=9400----
----主进程-pid=9400----
----主进程-pid=9400----  //停一会先做回调函数的事,做完在做自己的事
---callback func--pid=9400
---callback func--args=hahah
----主进程-pid=9400----
----主进程-pid=9400----
...


1、callback为回调函数,当子进程完成后,系统告诉主进程去做回调函数的事
2、子进程返回的值给回调函数








GIL问题:
Python多线程是假的需要依赖GIL

解决方案:
1、关键部分使用c语言或者其他语言写
2、使用多进程代替多线程

Python多进程是真的

猜你喜欢

转载自blog.csdn.net/scc_722/article/details/80468564
今日推荐