python的进程与线程

一、进程与线程的相关概念

1、什么是进程

进程是一个程序在一个数据集上的一次动态执行过程。

进程一般由程序,数据集,进程控制块三部分组成。

2、什么是线程

线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能。线程没有自己的系统资源。

3、线程与进程的关系

线程不能独立存在,一个线程肯定有一个父进程。进程也不能独立存在,它一定也包含一个进程。可以理解为进程是线程的容器。进程可以包含多个线程,但是线程只能依托一个进程存在。

二、python的线程模块threading

threading模块是建立在thread模块之上。thread模块以低级、原始的方式来处理和控制线程,而threading模块是对thread的二次封装。

注意:全局解释器锁GIL,决定了python的多线程只能用到一个cpu。

1、下面是两种threading的调用方式:

(1)、直接使用方式例子

 1 import threading
 2
 3 def func(num):
 4     print("Hi %s threading" % num)
 5
 6
 7 t1  = threading.Thread(target=func,args=(1,))
 8 t2  = threading.Thread(target=func,args=(2,))
 9 t1.start()
10 t2.start()

直接使用threading模块的方式

(2)、继承threading的方式

 1 import threading
 2
 3 class Mythread(threading.Thread):
 4     def __init__(self,num):
 5         threading.Thread.__init__(self)
 6         self.num = num
 7
 8     def run(self):
 9         print("Hi %s threading" % self.num)
10
11
12 t1  = Mythread(1)
13 t2  = Mythread(2)
14 t1.start()
15 t2.start()

类的继承方式

2、threading模块的join(),setDaemon()方法

join():在子线程完成运行之前,这个子线程的父线程将被阻塞。

setDaemon(True):将线程声明为守护线程,必须在start()方法之前进行设置。当线程设置为守护线程的话,不管线程任务有没有执行完,主线程执行完毕后都会随着主线程一起退出。

例子:

 1 import threading,time
 2
 3 def to_sing(name):
 4     print("Come to sing %s" % name)
 5     time.sleep(5)
 6
 7 def to_dance(name):
 8     print("Come to dance %s" % name)
 9     time.sleep(3)
10
11 if __name__=="__main__":
12     sing = threading.Thread(target=to_sing,args=("Tom",))
13     dance = threading.Thread(target=to_dance,args=("lisa",))
14
15     sing.start()
16     dance.start()
17     sing.join()
18     dance.join() #join方法放在这里的话起到的效果就是后面的print会等到sing方法和dance方法都执行完以后才会执行。如果不放在这里的话效果又是不一样的。
19     print("party is end...")

join()方法使用

 1 import threading,time
 2
 3 def to_sing(name):
 4     print("Come to sing %s" % name)
 5     time.sleep(5)
 6
 7 def to_dance(name):
 8     print("Come to dance %s" % name)
 9     time.sleep(30)
10
11 if __name__=="__main__":
12     sing = threading.Thread(target=to_sing,args=("Tom",))
13     dance = threading.Thread(target=to_dance,args=("lisa",))
14
15     sing.start()
16     dance.setDaemon(True) #必须放在start前面,这个例子就会出现运行五秒后dance的线程就和主线程一起退出了,因为设置它被设置成了守护线程。
17     dance.start()
18     sing.join()
19     print("party is end...") 

setDaemon()使用

其他的方法:

# run():  线程被cpu调度后自动执行线程对象的run方法
# start():启动线程活动。
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。

threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

3、同步锁,递归锁

(1)、不用同步锁的情况下,开启多线程

 1 import time
 2 import threading
 3
 4 def addNum():
 5     global num
 6
 7     temp = num
 8     time.sleep(0.0001)
 9     num = temp-1
10
11 num = 100
12 thread_list = []
13 for i in range(100):
14     t = threading.Thread(target=addNum)
15     t.start()
16     thread_list.append(t)
17
18 for j in thread_list:
19     j.join()
20
21 print("final num",num)
22
23 #终端打印
24 final num 93

不用同步锁的情况出现的问题

这个例子说明:当多线程操作同一个对象的时候很容易出现取到相同值的问题

使用同步锁解决问题

 1 import time
 2 import threading
 3
 4 def addNum():
 5     global num
 6     r.acquire() #将运行过程锁住
 7     temp = num
 8     time.sleep(0.0001)
 9     num = temp-1
10     r.release() #释放锁
11
12 num = 100
13 thread_list = []
14
15 r = threading.Lock()  #拿到多线程的锁
16 for i in range(100):
17     t = threading.Thread(target=addNum)
18     t.start()
19     thread_list.append(t)
20
21 for j in thread_list:
22     j.join()
23
24 print("final num",num)
25
26 #打印
27 final num 0

用同步所来解决问题的例子

这个例子也可以说明一个问题,用同步锁的情况下就是我拿到了数据,在我执行完逻辑之前,其他线程等待。是一个串行操作了。

(2)、使用多把同步锁造成死锁的例子和使用递归锁解决死锁的问题

 1 import time
 2 import threading
 3
 4 class MyThread(threading.Thread):
 5     def task_A(self):
 6         lock_A.acquire()
 7         print(self.name,"gotLockA",time.ctime())
 8         time.sleep(2)
 9         lock_B.acquire()
10         print(self.name,"gotLockB",time.ctime())
11         lock_B.release()
12         lock_A.release()
13
14
15     def task_B(self):
16         lock_B.acquire()
17         print(self.name, "gotLockB", time.ctime())
18         time.sleep(2)
19         lock_A.acquire()
20         print(self.name, "gotLockA", time.ctime())
21         lock_A.release()
22         lock_B.release()
23
24     def run(self):
25         self.task_A()
26         self.task_B()
27
28 lock_A = threading.Lock()
29 lock_B = threading.Lock()
30
31 t = []
32 for i in range(5):
33     t.append(MyThread())
34 for i in t :
35     i.start()
36 for j in t:
37     j.join()
38
39 #例子执行的流程
40 #开起5个线程,当第一个线程执行A,同时用了a锁和b锁,这个时候其他线程都在等待,当A线程执行完task_A释放了a、b锁后,执行任务task_B,拿到了B锁。这时线程B拿到了a锁把任务锁住了,等待b锁释放,但是b锁已经被A线程拿到,所以造成了死锁。 

多把同步锁造成死锁

使用递归锁来解决以上问题

 1 import time
 2 import threading
 3
 4 class MyThread(threading.Thread):
 5     def task_A(self):
 6         lock_R.acquire()
 7         print(self.name,"gotLockA",time.ctime())
 8         time.sleep(2)
 9         lock_R.acquire()
10         print(self.name,"gotLockB",time.ctime())
11         lock_R.release()
12         lock_R.release()
13
14
15     def task_B(self):
16         lock_R.acquire()
17         print(self.name, "gotLockB", time.ctime())
18         time.sleep(2)
19         lock_R.acquire()
20         print(self.name, "gotLockA", time.ctime())
21         lock_R.release()
22         lock_R.release()
23
24     def run(self):
25         self.task_A()
26         self.task_B()
27
28 # lock_A = threading.Lock()
29 # lock_B = threading.Lock()
30 lock_R = threading.RLock() #用递归锁代替同步锁
31
32 t = []
33 for i in range(5):
34     t.append(MyThread())
35 for i in t :
36     i.start()
37 for j in t:
38     j.join()

递归锁解决死锁问题

从上面一个例子来看,说明当任务拿到了递归锁,那么其他线程任务就只能等待,拿到递归锁的任务线程所有任务执行完毕后再争抢这把锁来让自己执行任务了。

递归锁:为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

4、同步条件

 1 import threading,time
 2
 3 class Boss(threading.Thread):
 4     def run(self):
 5         print("Boss:Tonight we mast working to 11:00")
 6         event.set()
 7         print(event.isSet())
 8         time.sleep(5)
 9         print("Boss:ok! we can go home.")
10         print(event.isSet())
11         event.set()
12
13 class Worker(threading.Thread):
14     def run(self):
15         event.wait()
16         print("Worker:Oh my god!")
17         time.sleep(1)
18         event.clear()
19         event.wait()
20         print("Worker:OhYeah!")
21
22 if __name__ == "__main__":
23     event = threading.Event()
24     threads = []
25     for i in range(5):
26         threads.append(Worker())
27     threads.append(Boss())
28     for t in threads:
29         t.start()
30     for t in threads:
31         t.join()

同步条件例子

例子讲解:5个工人线程启动的时候,会监听等待一个事件,当老板线程启动了,时间被设定为True,5个工人线程收到事件后做出反映,然后清楚本次事件,再监听新的事件。

5、信号量

信号量是用来控制线程并发数的,BoundedSemaphore或Semaphonre管理一个内置的计数器,每当调用acquired()时-1,调用released()时+1

计数器不能小于0,当计数器为0时,acquire()将阻塞线程至同步锁定状态,知道其他线程调用released().

BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。

 1 import threading,time
 2
 3 class myThread(threading.Thread):
 4     def run(self):
 5         if semaphore.acquire():
 6             print(self.name)
 7             time.sleep(3)
 8             semaphore.release()
 9
10 if __name__ == "__main__":
11     semaphore = threading.Semaphore(5)
12     t = []
13     for i in range(100):
14         t.append(myThread())
15     for j in t:
16         j.start()

信号量例子

6、队列

队列的方法

 1 创建一个“队列”对象
 2 import Queue
 3 q = Queue.Queue(maxsize = 10)
 4 Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。
 5
 6 将一个值放入队列中
 7 q.put(10)
 8 调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为
 9 1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。
10
11 将一个值从队列中取出
12 q.get()
13 调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,
14 get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。
15
16 Python Queue模块有三种队列及构造函数:
17 1、Python Queue模块的FIFO队列先进先出。   class queue.Queue(maxsize)
18 2、LIFO类似于堆,即先进后出。               class queue.LifoQueue(maxsize)
19 3、还有一种是优先级队列级别越低越先出来。        class queue.PriorityQueue(maxsize)
20
21 此包中的常用方法(q = Queue.Queue()):
22 q.qsize() 返回队列的大小
23 q.empty() 如果队列为空,返回True,反之False
24 q.full() 如果队列满了,返回True,反之False
25 q.full 与 maxsize 大小对应
26 q.get([block[, timeout]]) 获取队列,timeout等待时间
27 q.get_nowait() 相当q.get(False)
28 非阻塞 q.put(item) 写入队列,timeout等待时间
29 q.put_nowait(item) 相当q.put(item, False)
30 q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
31 q.join() 实际上意味着等到队列为空,再执行别的操作

队列有两种工作方式,一个是先进先出,一个是先进后出。大多数使用场景都是先进先出场景。

用队列来实现生产者消费者模型

 1 import time,random
 2 import queue,threading
 3
 4 q = queue.Queue()
 5
 6 def Producer(name):
 7     count = 0
 8     while count<100:
 9         print("making .....")
10         time.sleep(random.randrange(4))
11         q.put(count)
12         print("Producer %s has produced %s baozi.." % (name,count))
13         count += 1
14         print("ok....")
15
16 def Consumer(name):
17     count = 0
18     while count < 100:
19         time.sleep(random.randrange(3))
20         if not q.empty():
21             data = q.get()
22             # q.task_done()
23             # q.join()
24             print(data)
25             print(‘\033[32;1mConsumer %s has eat %s baozi...\033[0m‘ % (name, data))
26         else:
27             print("-----no baozi anymore----")
28         count += 1
29
30 p1 = threading.Thread(target=Producer,args=("A",))
31 c1 = threading.Thread(target=Consumer,args=("B",))
32
33 p1.start()
34 c1.start()

吃包子

三、进程

1、进程的两种调用方式(和线程类似)

(1)、直接调用

 1 from multiprocessing import Process
 2 import time
 3
 4 def foo(name):
 5     time.sleep(1)
 6     print("hello",name,time.ctime())
 7
 8 if __name__=="__main__":
 9     p_list=[]
10     for i in range(3):
11         p = Process(target=foo,args=(‘aaa‘,))
12         p_list.append(p)
13         p.start()
14     for i in p_list:
15         p.join()
16     print("end")

直接调用

(2)、类的继承方式调用

 1 from multiprocessing import Process
 2 import time
 3
 4 class MyProcess(Process):
 5     def __init__(self,num):
 6         super(MyProcess,self).__init__()
 7         self.num=num
 8
 9     def run(self):
10         time.sleep(1)
11         print("hello",self.num,time.ctime())
12
13 if __name__=="__main__":
14     p_list = []
15     for i in range(3):
16         p = MyProcess(i)
17         p.start()
18         p_list.append(p)
19     for p in p_list:
20         p.join()
21
22     print("end")

继承方式调用

2、Process类的方法

构造方法:
Process([group [, target [, name [, args [, kwargs]]]]])
  group: 线程组,目前还没有实现,库引用中提示必须是None; 
  target: 要执行的方法; 
  name: 进程名; 
  args/kwargs: 要传入方法的参数。
实例方法:
  is_alive():返回进程是否在运行。
  join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。
  start():进程准备就绪,等待CPU调度
  run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。
  terminate():不管任务是否完成,立即停止工作进程
属性:
  daemon:和线程的setDeamon功能一样
  name:进程名字。
  pid:进程号。

3、进程间通信

(1)进程队列Queue

注意:进程是大写的Queue模块,线程是小写的queue模块。

 1 from multiprocessing import Process,Queue
 2
 3 def foo(q,n):
 4     q.put(n*n+1)
 5     print("son process",id(q))
 6
 7 if __name__=="__main__":
 8     q = Queue()
 9     print("main process",id(q))
10
11     for i in range(3):
12         p = Process(target=foo,args=(q,i))
13         p.start()
14     print(q.get())
15     print(q.get())
16     print(q.get())

Queue例子

(2)管道Pipe()

 1 from multiprocessing import Process,Pipe
 2
 3 def foo(conn):
 4     conn.send([12,{"name":"ptq"},"hello"])
 5     response = conn.recv()
 6     print("response:",response)
 7     conn.close()
 8     print("q_ID2:", id(conn))
 9
10 if __name__=="__main__":
11     parent_conn,child_conn = Pipe()
12     print("q_ID1:", id(child_conn))
13     p1 = Process(target=foo,args=(child_conn,))
14     p1.start()
15     print("parent_conn",parent_conn.recv())
16     parent_conn.send("hello son process")
17     p1.join()

管道Pipe例子

(3)、共享内存Managers

 1 from multiprocessing import Process,Manager
 2
 3 def foo(d,l,i):
 4     d[i]=1
 5     d[‘2‘]=2
 6     l.append(i)
 7     print("son process:",id(d),id(l))
 8
 9 if __name__=="__main__":
10     with Manager() as manager:
11         d = manager.dict()
12         l = manager.list(range(5))
13         print("main process: ",id(d),id(l))
14         p_list = []
15         for i in range(10):
16             p = Process(target=foo,args=(d,l,i))
17             p.start()
18             p_list.append(p)
19
20         for p in p_list:
21             p.join()

Managers例子

(4)、进程池

进程池内部维护一个进程序列,当使用时,择取进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。

进程池的两个方法:

apply   这个方法效果就是串行的效果,不管池子里面有多少个进程可用。都是一个进程一个进程运行。

apply_async

 1 from multiprocessing import Process,Pool
 2 import time,os
 3
 4 def Foo(i):
 5     time.sleep(1)
 6     print(i)
 7     return i+100
 8
 9 def Bar(arg):#回调函数调用,参数是上一个函数的返回值
10     print(os.getpid())
11     print(os.getppid())
12     print("logger:",arg)
13
14 if __name__== "__main__":
15     pool = Pool(5)
16     # Bar(1)
17     print("-----------------")
18     for i in range(10):
19         pool.apply_async(func=Foo,args=(i,),callback=Bar)#这里的callback是回调函数。
20
21     #这里是固定写法,变了就会报错
22     pool.close()
23     pool.join()
24     print("end")

进程池例子

时间: 2024-12-14 04:32:15

python的进程与线程的相关文章

【Python】进程和线程

多任务的实现方式有三种方式: 1.多进程 2.多线程 3.多进程+多线程(这种比较复杂,实际很少采用) [多进程] 1.在mac中创建子进程使用Python封装的fork()系统调用. import os pid = os.fork() pid 2.在windows上的实现. 使用multiprocessing模块的Process类. 为了观察,还加了一下端代码~ # -*- coding: utf-8 -*- from multiprocessing import Process import

Python下进程与线程的原理及区别

对于所有的语言都可能会遇到进程和线程的问题,一般情况下线程是由进程产生的,一个进程产生多个线程来按照一定的规则(Python下根据CPU调度算法和全局进程锁)来利用CPU,我们称之为多线程模式:而一个进程在产生的同时,同时会生成一个主线程,如果程序生成多个进程,那么每个进程都会产生一个线程,多个程序按照一定的规则去利用CPU,我们称之为多进程模式.                 Python下多线程与多进程关系图原理如下所示:

Python(十) 进程、线程、协程篇

本节内容 1.操作系统发展史    2.进程.与线程区别    3.Python GIL全局解释器锁    4.线程        1.语法        2.join        3.线程锁 Lock.RLock.信号量        4.将线程变为守护进程        5.Event事件        6.queue队列        7.生产者消费者模型        8.Queue队列        9.开发一个线程池    5.进程        1.语法        2.进程间

python之进程、线程、协程的调度原理

进程.线程和协程的调度和运行原理总结. 进程.线程的调度策略介绍 linux的操作系统详细调度策略可参考:http://blog.csdn.net/gatieme/article/details/51872659 linux中的进程主要有三种调度策略: 优先级调度:将进程分为普通进程和实时进程: 先进先出(队列)调度:实时进程先创建的先执行,直到 轮转调度(时间片):达到一定的CPU执行时间后强制切换: 多进程程序的调度其实还是线程的调度,线程才是CPU调度的基本单位:在同一个进程内线程切换不会

《Python》进程收尾线程初识

from multiprocessing import Manager 把所有实现了数据共享的比较便捷的类都重新又封装了一遍,并且在原有的multiprocessing基础上增加了新的机制list.dict 机制:支持的数据类型非常有限 list.dict都不是数据安全的,需要自己加锁来保证数据安全 from multiprocessing import Manager,Process,Lock def work(d,lock): with lock: d['count'] -= 1 if __

Python之进程与线程

一.进程 1.什么是进程 程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本:进程是程序的一次执行活动,属于动态概念. 在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行.这是这样的设计,大大提高了CPU的利用率.进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的. 2.有了进程为什么还要线程? 进程有很

python学习进程和线程中碰到的问题以及问题解惑

python中的os.getpid() os.getpid()是获取的是当前进程的进程号,os.getppid()是获取当前进程的父进程的进程号 python 中__name__ = '__main__' 的作用 eg: def main(): print "we are in %s"%__name__ if __name__ == '__main__': main() 这个函数定义了一个main函数,我们执行一下该py文件发现结果是打印出"we are in __main_

Python之进程、线程、协程篇

本章内容: 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 线程 Threading用于提供线程相关的操作.线程是应用程序中工作的最小单元,它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. threading 模块建立在 _thread 模块之上.thread 模块以低级.原始的方式来处理和控制线程,而 threading

【Python】[进程和线程]多进程,多线程,ThreadLocal,进程VS.线程,分布式进程

1.多进程,multiprocessing模块,   进程间的通信:Queue[队列],Pipes[管子]2.多线程,    注意:线程公用变量,混乱   解决方法Lock:因为只有一个锁,所以当要执行统一个函数的时候,只有在解锁的前提下才能 执行. balance = 0 lock = threading.Lock() def run_thread(n): for i in range(100000): # 先要获取锁: lock.acquire() try: # 放心地改吧: change_