python高级之多线程

本节内容

  1. 线程与进程定义及区别
  2. python全局解释器锁
  3. 线程的定义及使用
  4. 互斥锁
  5. 线程死锁和递归锁
  6. 条件变量同步(Condition)
  7. 同步条件(Event)
  8. 信号量
  9. 队列Queue
  10. Python中的上下文管理器(contextlib模块)
  11. 自定义线程池

1.线程与进程定义及区别

线程的定义:

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

进程的定义:

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体。程序是指令、数据及其组织形式的描述,进程是程序的实体。

进程和线程的区别

  • Threads share the address space of the process that created it; processes have their own address space.
  • 线程的地址空间共享,每个进程有自己的地址空间。
  • Threads have direct access to the data segment of its process; processes have their own copy of the data segment of the parent process.
  • 一个进程中的线程直接接入他的进程的数据段,但是每个进程都有他们自己的从父进程拷贝过来的数据段
  • Threads can directly communicate with other threads of its process; processes must use interprocess communication to communicate with sibling processes.
  • 一个进程内部的线程之间能够直接通信,进程之间必须使用进程间通信实现通信
  • New threads are easily created; new processes require duplication of the parent process.
  • 新的线程很容易被创建,新的进程需要从父进程复制
  • Threads can exercise considerable control over threads of the same process; processes can only exercise control over child processes.
  • 一个进程中的线程间能够有相当大的控制力度,进程仅仅只能控制他的子进程
  • Changes to the main thread (cancellation, priority change, etc.) may affect the behavior of the other threads of the process; changes to the parent process does not affect child processes.
  • 改变主线程(删除,优先级改变等)可能影响这个进程中的其他线程;修改父进程不会影响子进程

2.python全局解释器锁

全局解释器锁又叫做GIL

python目前有很多解释器,目前使用最广泛的是CPython,还有PYPY和JPython等解释器,但是使用最广泛的还是CPython解释器,而对于全局解释器锁来说,就是在CPython上面才有的,它的原理是在解释器层面加上一把大锁,保证同一时刻只能有一个python线程在解释器中执行。

对于计算密集型的python多线程来说,无法利用到多线程带来的效果, 在2.7时计算密集型的python多线程执行效率比顺序执行的效率还低的多,在python3.5中对这种情况进行了优化,基本能实现这种多线程执行时间和顺序执行时间差不多的效果。

对于I/O密集型的python多线程来说,GIL的影响不是很大,因为I/O密集型的python多线程进程,每个线程在等待I/O的时候,将会释放GIL资源,供别的线程来抢占。所以对于I/O密集型的python多线程进程来说,还是能比顺序执行的效率要高的。

python的GIL这个东西。。。比较恶心,但是由于CPython解释器中的很多东西都是依赖这个东西开发的,如果改的话,将是一件浩大的工程。。。所以到现在还是存在这个问题,这也是python最为别人所诟病的地方。。。

3.线程的定义及使用

线程的两种调用方式

线程的调用有两种方式,分为直接调用和继承式调用,示例代码如下:

  1. #直接调用
  2. import threading
  3. import time
  4.  
  5. def sayhi(num): #定义每个线程要运行的函数
  6.  
  7. print("running on number:%s" %num)
  8.  
  9. time.sleep(3)
  10.  
  11. if __name__ == '__main__':
  12.  
  13. t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
  14. t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例
  15.  
  16. t1.start() #启动线程
  17. t2.start() #启动另一个线程
  18.  
  19. print(t1.getName()) #获取线程名
  20. print(t2.getName())
  21.  
  22. #继承式调用
  23. import threading
  24. import time
  25.  
  26. class MyThread(threading.Thread):
  27. def __init__(self,num):
  28. threading.Thread.__init__(self)
  29. self.num = num
  30.  
  31. def run(self):#定义每个线程要运行的函数
  32.  
  33. print("running on number:%s" %self.num)
  34.  
  35. time.sleep(3)
  36.  
  37. if __name__ == '__main__':
  38.  
  39. t1 = MyThread(1)
  40. t2 = MyThread(2)
  41. t1.start()
  42. t2.start()

可以看到直接调用是导入threading模块并定义一个函数,之后实例化threading.Thread类的时候,将刚定义的函数名通过target参数传递进去,然后调用实例的start()方法启动一个线程。

而继承式调用是创建一个类继承自threading.Thread类,并在构造方法中调用父类的构造方法,之后重写run方法,run方法中就是每个线程起来之后执行的内容,就类似于前面通过target参数传递进去的函数。之后以这个继承的类来创建对象,并执行对象的start()方法启动一个线程。

从这里可以看出,其实。。。直接调用通过使用target参数把函数带进类里面之后应该是用这个函数替代了run方法。

join和setDaemon

join()方法在该线程对象启动了之后调用线程的join()方法之后,那么主线程将会阻塞在当前位置直到子线程执行完成才继续往下走,如果所有子线程对象都调用了join()方法,那么主线程将会在等待所有子线程都执行完之后再往下执行。

setDaemon(True)方法在子线程对象调用start()方法(启动该线程)之前就调用的话,将会将该子线程设置成守护模式启动,这是什么意思呢?当子线程还在运行的时候,父线程已经执行完了,如果这个子线程设置是以守护模式启动的,那么随着主线程执行完成退出时,子线程立马也退出,如果没有设置守护启动子线程(也就是正常情况下)的话,主线程执行完成之后,进程会等待所有子线程执行完成之后才退出。

示例代码如下:

  1. import threading
  2. from time import ctime,sleep
  3. import time
  4.  
  5. def music(func):
  6. for i in range(2):
  7. print ("Begin listening to %s. %s" %(func,ctime()))
  8. sleep(4)
  9. print("end listening %s"%ctime())
  10.  
  11. def move(func):
  12. for i in range(2):
  13. print ("Begin watching at the %s! %s" %(func,ctime()))
  14. sleep(5)
  15. print('end watching %s'%ctime())
  16.  
  17. threads = []
  18. t1 = threading.Thread(target=music,args=('七里香',))
  19. threads.append(t1)
  20. t2 = threading.Thread(target=move,args=('阿甘正传',))
  21. threads.append(t2)
  22.  
  23. if __name__ == '__main__':
  24.  
  25. for t in threads:
  26. # t.setDaemon(True)
  27. t.start()
  28. # t.join()
  29. # t1.join()
  30. t2.join()########考虑这三种join位置下的结果?
  31. print ("all over %s" %ctime())

4.互斥锁

互斥锁的产生是因为前面提到过多线程之间是共享同一块内存地址的,也就是说多个不同的线程能够访问同一个变量中的数据,那么,当多个线程要修改这个变量,会产生什么情况呢?当多个线程修改同一个数据的时候,如果操作的时间够短的话,能得到我们想要的结果,但是,如果修改数据不是原子性的(这中间的时间太长)的话。。。很有可能造成数据的错误覆盖,从而得到我们不想要的结果。例子如下:

  1. import time
  2. import threading
  3.  
  4. def addNum():
  5. global num #在每个线程中都获取这个全局变量
  6. # num-=1 # 如果是这种情况,那么操作时间足够短,类似于原子操作了,所以,能够得到我们想要的结果
  7.  
  8. temp=num
  9. print('--get num:',num ) # 因为print会调用终端输出,终端是一个设备,相当于要等待终端I/O就绪之后才能输出打印内容,在等待终端I/O的过程中,该线程已经挂起。。。这时其他线程获取到的是没被改变之前的num值,之后该线程I/O就绪之后切换回来,对num-1了,其他线程在I/O就绪之后也在没被改变之前的num基础上减一,这样。。。就得到了我们不想看到的结果。。。
  10. #time.sleep(0.1) # sleep也能达到相同的效果,执行到sleep时,该线程直接进入休眠状态,释放了GIL直到sleep时间过去。
  11. num =temp-1 #对此公共变量进行-1操作
  12.  
  13. num = 100 #设定一个共享变量
  14. thread_list = []
  15. for i in range(100):
  16. t = threading.Thread(target=addNum)
  17. t.start()
  18. thread_list.append(t)
  19.  
  20. for t in thread_list: #等待所有线程执行完毕
  21. t.join()
  22.  
  23. print('final num:', num )
  24. 这时候,就需要互斥锁出场了,前面出现的num可以被称作临界资源(会被多个线程同时访问),为了让临界资源能够实现按照我们控制访问,需要使用互斥锁来锁住临界资源,当一个线程需要访问临界资源时先检查这个资源有没有被锁住,如果没有被锁住,那么访问这个资源并同时给这个资源加上锁,这样别的线程就无法访问该临界资源了,直到这个线程访问完了这个临界资源之后,释放这把锁,其他线程才能够抢占该临界资源。这个,就是互斥锁的概念。 示例代码:
  25.  
  26. import time
  27. import threading
  28.  
  29. def addNum():
  30. global num #在每个线程中都获取这个全局变量
  31. # num-=1
  32. lock.acquire() # 检查互斥锁,如果没锁住,则锁住并往下执行,如果检查到锁住了,则挂起等待锁被释放时再抢占。
  33. temp=num
  34. print('--get num:',num )
  35. #time.sleep(0.1)
  36. num =temp-1 #对此公共变量进行-1操作
  37. lock.release() # 释放该锁
  38.  
  39. num = 100 #设定一个共享变量
  40. thread_list = []
  41. lock=threading.Lock() # 定义互斥锁
  42.  
  43. for i in range(100):
  44. t = threading.Thread(target=addNum)
  45. t.start()
  46. thread_list.append(t)
  47.  
  48. for t in thread_list: #等待所有线程执行完毕
  49. t.join()
  50.  
  51. print('final num:', num )

互斥锁与GIL的关系?

Python的线程在GIL的控制之下,线程之间,对整个python解释器,对python提供的C API的访问都是互斥的,这可以看作是Python内核级的互斥机制。但是这种互斥是我们不能控制的,我们还需要另外一种可控的互斥机制———用户级互斥。内核级通过互斥保护了内核的共享资源,同样,用户级互斥保护了用户程序中的共享资源。

GIL 的作用是:对于一个解释器,只能有一个thread在执行bytecode。所以每时每刻只有一条bytecode在被执行一个thread。GIL保证了bytecode 这层面上是thread safe的。 但是如果你有个操作比如 x += 1,这个操作需要多个bytecodes操作,在执行这个操作的多条bytecodes期间的时候可能中途就换thread了,这样就出现了data races的情况了。

5.线程死锁和递归锁

如果公共的临界资源比较多,并且线程间都使用互斥锁去访问临界资源,那么将有可能出现一个情况:

  • 线程1拿到了资源A,接着需要资源B才能继续执行下去
  • 线程2拿到了资源B,接着需要资源A才能继续执行下去

这样,线程1和线程2互不相让。。。结果就都卡死在这了,这就是线程死锁的由来。。。

示例代码如下:

  1. import threading,time
  2.  
  3. class myThread(threading.Thread):
  4. def doA(self):
  5. lockA.acquire() # 锁住A资源
  6. print(self.name,"gotlockA",time.ctime())
  7. time.sleep(3)
  8. lockB.acquire() # 锁住B资源
  9. print(self.name,"gotlockB",time.ctime())
  10. lockB.release() # 解锁B资源
  11. lockA.release() # 解锁A资源
  12.  
  13. def doB(self):
  14. lockB.acquire()
  15. print(self.name,"gotlockB",time.ctime())
  16. time.sleep(2)
  17. lockA.acquire()
  18. print(self.name,"gotlockA",time.ctime())
  19. lockA.release()
  20. lockB.release()
  21. def run(self):
  22. self.doA()
  23. self.doB()
  24. if __name__=="__main__":
  25.  
  26. lockA=threading.Lock()
  27. lockB=threading.Lock()
  28. threads=[]
  29. for i in range(5):
  30. threads.append(myThread())
  31. for t in threads:
  32. t.start()
  33. for t in threads:
  34. t.join() # 等待线程结束

那么,怎么解决这个问题呢?python中提供了一个方法(不止python中,基本上所有的语言中都支持这个方法)那就是递归锁。递归锁的创建是使用threading.RLock(),它里面其实维护了两个东西,一个是Lock,另一个是counter,counter记录了加锁的次数,每加一把锁,counter就会+1,释放一次锁counter就会减一,直到所有加的锁都被释放掉了之后其他线程才能够访问这把锁获取资源。当然这个限制是对于线程之间的,同一个线程中,只要这个线程抢到了这把锁,那么这个线程就可以对这把锁加多个锁,而不会阻塞自己的执行。这就是递归锁的原理。

示例代码:

  1. import time
  2. import threading
  3. class Account:
  4. def __init__(self, _id, balance):
  5. self.id = _id
  6. self.balance = balance
  7. self.lock = threading.RLock()
  8.  
  9. def withdraw(self, amount):
  10. with self.lock: # 会将包裹的这块代码用锁保护起来,直到这块代码执行完成之后,这把锁就会被释放掉
  11. self.balance -= amount
  12.  
  13. def deposit(self, amount):
  14. with self.lock:
  15. self.balance += amount
  16.  
  17. def drawcash(self, amount):#lock.acquire中嵌套lock.acquire的场景
  18.  
  19. with self.lock:
  20. interest=0.05
  21. count=amount+amount*interest
  22.  
  23. self.withdraw(count)
  24.  
  25. def transfer(_from, to, amount):
  26.  
  27. #锁不可以加在这里 因为其他的线程执行的其它方法在不加锁的情况下数据同样是不安全的
  28. _from.withdraw(amount)
  29. to.deposit(amount)
  30.  
  31. alex = Account('alex',1000)
  32. yuan = Account('yuan',1000)
  33.  
  34. t1=threading.Thread(target = transfer, args = (alex,yuan, 100))
  35. t1.start()
  36.  
  37. t2=threading.Thread(target = transfer, args = (yuan,alex, 200))
  38. t2.start()
  39.  
  40. t1.join()
  41. t2.join()
  42.  
  43. print('>>>',alex.balance)
  44. print('>>>',yuan.balance)

6.条件变量同步(Condition)

有一类线程需要满足条件之后才能够继续执行,Python提供了threading.Condition 对象用于条件变量线程的支持,它除了能提供RLock()或Lock()的方法外,还提供了 wait()、notify()、notifyAll()方法。

lock_con=threading.Condition([Lock/Rlock]): 锁是可选选项,不传人锁,对象自动创建一个RLock()。

  1. wait():条件不满足时调用,线程会释放锁并进入等待阻塞;
  2. notify():条件创造后调用,通知等待池激活一个线程;
  3. notifyAll():条件创造后调用,通知等待池激活所有线程。

示例代码:

  1. import threading,time
  2. from random import randint
  3. class Producer(threading.Thread):
  4. def run(self):
  5. global L
  6. while True:
  7. val=randint(0,100)
  8. print('生产者',self.name,":Append"+str(val),L)
  9. if lock_con.acquire():
  10. L.append(val)
  11. lock_con.notify()
  12. lock_con.release()
  13. time.sleep(3)
  14. class Consumer(threading.Thread):
  15. def run(self):
  16. global L
  17. while True:
  18. lock_con.acquire()
  19. if len(L)==0:
  20. lock_con.wait()
  21. print('消费者',self.name,":Delete"+str(L[0]),L)
  22. del L[0]
  23. lock_con.release()
  24. time.sleep(0.25)
  25.  
  26. if __name__=="__main__":
  27.  
  28. L=[]
  29. lock_con=threading.Condition()
  30. threads=[]
  31. for i in range(5):
  32. threads.append(Producer())
  33. threads.append(Consumer())
  34. for t in threads:
  35. t.start()
  36. for t in threads:
  37. t.join()

7.同步条件(Event)

条件同步和条件变量同步差不多意思,只是少了锁功能,因为条件同步设计于不访问共享资源的条件环境。event=threading.Event():条件环境对象,初始值 为False;

  1. event.isSet():返回event的状态值;
  2. event.wait():如果 event.isSet()==False将阻塞线程;
  3. event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
  4. event.clear():恢复event的状态值为False。

例子1:

  1. import threading,time
  2. class Boss(threading.Thread):
  3. def run(self):
  4. print("BOSS:今晚大家都要加班到22:00。")
  5. event.isSet() or event.set()
  6. time.sleep(5)
  7. print("BOSS:<22:00>可以下班了。")
  8. event.isSet() or event.set()
  9. class Worker(threading.Thread):
  10. def run(self):
  11. event.wait()
  12. print("Worker:哎……命苦啊!")
  13. time.sleep(0.25)
  14. event.clear()
  15. event.wait()
  16. print("Worker:OhYeah!")
  17. if __name__=="__main__":
  18. event=threading.Event()
  19. threads=[]
  20. for i in range(5):
  21. threads.append(Worker())
  22. threads.append(Boss())
  23. for t in threads:
  24. t.start()
  25. for t in threads:
  26. t.join()

例子2:

  1. import threading,time
  2. import random
  3. def light():
  4. if not event.isSet():
  5. event.set() #wait就不阻塞 #绿灯状态
  6. count = 0
  7. while True:
  8. if count < 10:
  9. print('\033[42;1m--green light on---\033[0m')
  10. elif count <13:
  11. print('\033[43;1m--yellow light on---\033[0m')
  12. elif count <20:
  13. if event.isSet():
  14. event.clear()
  15. print('\033[41;1m--red light on---\033[0m')
  16. else:
  17. count = 0
  18. event.set() #打开绿灯
  19. time.sleep(1)
  20. count +=1
  21. def car(n):
  22. while 1:
  23. time.sleep(random.randrange(10))
  24. if event.isSet(): #绿灯
  25. print("car [%s] is running.." % n)
  26. else:
  27. print("car [%s] is waiting for the red light.." %n)
  28. if __name__ == '__main__':
  29. event = threading.Event()
  30. Light = threading.Thread(target=light)
  31. Light.start()
  32. for i in range(3):
  33. t = threading.Thread(target=car,args=(i,))
  34. t.start()

8.信号量

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

计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)

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

例子:

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

9.队列Queue

使用队列方法:

  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。如果队列当前为空且block1put()方法就使调用线程暂停,直到空出一个数据单元。如果block0put方法将引发Full异常。
  10.  
  11. 将一个值从队列中取出
  12. q.get()
  13. 调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且blockTrueget()就使调用线程暂停,直至有项目可用。如果队列为空且blockFalse,队列将引发Empty异常。
  14.  
  15. Python Queue模块有三种队列及构造函数:
  16. 1Python Queue模块的FIFO队列先进先出。 class queue.Queue(maxsize)
  17. 2LIFO类似于堆,即先进后出。 class queue.LifoQueue(maxsize)
  18. 3、还有一种是优先级队列级别越低越先出来。 class queue.PriorityQueue(maxsize)
  19.  
  20. 此包中的常用方法(q = Queue.Queue()):
  21. q.qsize() 返回队列的大小
  22. q.empty() 如果队列为空,返回True,反之False
  23. q.full() 如果队列满了,返回True,反之False
  24. q.full maxsize 大小对应
  25. q.get([block[, timeout]]) 获取队列,timeout等待时间
  26. q.get_nowait() 相当q.get(False)
  27. 非阻塞 q.put(item) 写入队列,timeout等待时间
  28. q.put_nowait(item) 相当q.put(item, False)
  29. q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
  30. q.join() 实际上意味着等到队列为空,再执行别的操作

例子集合:

  1. #例子1:
  2. import threading,queue
  3. from time import sleep
  4. from random import randint
  5. class Production(threading.Thread):
  6. def run(self):
  7. while True:
  8. r=randint(0,100)
  9. q.put(r)
  10. print("生产出来%s号包子"%r)
  11. sleep(1)
  12. class Proces(threading.Thread):
  13. def run(self):
  14. while True:
  15. re=q.get()
  16. print("吃掉%s号包子"%re)
  17. if __name__=="__main__":
  18. q=queue.Queue(10)
  19. threads=[Production(),Production(),Production(),Proces()]
  20. for t in threads:
  21. t.start()
  22.  
  23. #例子2
  24. import time,random
  25. import queue,threading
  26. q = queue.Queue()
  27. def Producer(name):
  28. count = 0
  29. while count <20:
  30. time.sleep(random.randrange(3))
  31. q.put(count)
  32. print('Producer %s has produced %s baozi..' %(name, count))
  33. count +=1
  34. def Consumer(name):
  35. count = 0
  36. while count <20:
  37. time.sleep(random.randrange(4))
  38. if not q.empty():
  39. data = q.get()
  40. print(data)
  41. print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))
  42. else:
  43. print("-----no baozi anymore----")
  44. count +=1
  45. p1 = threading.Thread(target=Producer, args=('A',))
  46. c1 = threading.Thread(target=Consumer, args=('B',))
  47. p1.start()
  48. c1.start()
  49.  
  50. #例子3
  51. #实现一个线程不断生成一个随机数到一个队列中(考虑使用Queue这个模块)
  52. # 实现一个线程从上面的队列里面不断的取出奇数
  53. # 实现另外一个线程从上面的队列里面不断取出偶数
  54.  
  55. import random,threading,time
  56. from queue import Queue
  57. #Producer thread
  58. class Producer(threading.Thread):
  59. def __init__(self, t_name, queue):
  60. threading.Thread.__init__(self,name=t_name)
  61. self.data=queue
  62. def run(self):
  63. for i in range(10): #随机产生10个数字 ,可以修改为任意大小
  64. randomnum=random.randint(1,99)
  65. print ("%s: %s is producing %d to the queue!" % (time.ctime(), self.getName(), randomnum))
  66. self.data.put(randomnum) #将数据依次存入队列
  67. time.sleep(1)
  68. print ("%s: %s finished!" %(time.ctime(), self.getName()))
  69.  
  70. #Consumer thread
  71. class Consumer_even(threading.Thread):
  72. def __init__(self,t_name,queue):
  73. threading.Thread.__init__(self,name=t_name)
  74. self.data=queue
  75. def run(self):
  76. while 1:
  77. try:
  78. val_even = self.data.get(1,5) #get(self, block=True, timeout=None) ,1就是阻塞等待,5是超时5秒
  79. if val_even%2==0:
  80. print ("%s: %s is consuming. %d in the queue is consumed!" % (time.ctime(),self.getName(),val_even))
  81. time.sleep(2)
  82. else:
  83. self.data.put(val_even)
  84. time.sleep(2)
  85. except: #等待输入,超过5秒 就报异常
  86. print ("%s: %s finished!" %(time.ctime(),self.getName()))
  87. break
  88. class Consumer_odd(threading.Thread):
  89. def __init__(self,t_name,queue):
  90. threading.Thread.__init__(self, name=t_name)
  91. self.data=queue
  92. def run(self):
  93. while 1:
  94. try:
  95. val_odd = self.data.get(1,5)
  96. if val_odd%2!=0:
  97. print ("%s: %s is consuming. %d in the queue is consumed!" % (time.ctime(), self.getName(), val_odd))
  98. time.sleep(2)
  99. else:
  100. self.data.put(val_odd)
  101. time.sleep(2)
  102. except:
  103. print ("%s: %s finished!" % (time.ctime(), self.getName()))
  104. break
  105. #Main thread
  106. def main():
  107. queue = Queue()
  108. producer = Producer('Pro.', queue)
  109. consumer_even = Consumer_even('Con_even.', queue)
  110. consumer_odd = Consumer_odd('Con_odd.',queue)
  111. producer.start()
  112. consumer_even.start()
  113. consumer_odd.start()
  114. producer.join()
  115. consumer_even.join()
  116. consumer_odd.join()
  117. print ('All threads terminate!')
  118.  
  119. if __name__ == '__main__':
  120. main()
  121.  
  122. #注意:列表是线程不安全的
  123. #例子4
  124. import threading,time
  125.  
  126. li=[1,2,3,4,5]
  127.  
  128. def pri():
  129. while li:
  130. a=li[-1]
  131. print(a)
  132. time.sleep(1)
  133. try:
  134. li.remove(a)
  135. except:
  136. print('----',a)
  137.  
  138. t1=threading.Thread(target=pri,args=())
  139. t1.start()

源码


  1. t2=threading.Thread(target=pri,args=())
  2. t2.start()

python高级之多线程的更多相关文章

  1. 第十章:Python高级编程-多线程、多进程和线程池编程

    第十章:Python高级编程-多线程.多进程和线程池编程 Python3高级核心技术97讲 笔记 目录 第十章:Python高级编程-多线程.多进程和线程池编程 10.1 Python中的GIL 10 ...

  2. 第七篇: python高级之多线程

    21 interest=0.05 22 count=amount+amount*interest 23 24 self.withdraw(count) 25 26 27 def transfer(_f ...

  3. Python高级编程-多线程

    (一)进程线程概述: 很多同学都听说过,现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统. 什么叫“多任务”呢?简单地说,就是操作系统可以同时运行 ...

  4. python高级之网络编程

    python高级之网络编程 本节内容 网络通信概念 socket编程 socket模块一些方法 聊天socket实现 远程执行命令及上传文件 socketserver及其源码分析 1.网络通信概念 说 ...

  5. python高级之多进程

    python高级之多进程 本节内容 多进程概念 Process类 进程间通讯 进程同步 进程池 1.多进程概念 multiprocessing is a package that supports s ...

  6. 第八篇:python高级之多进程

    python高级之多进程   python高级之多进程 本节内容 多进程概念 Process类 进程间通讯 进程同步 进程池 1.多进程概念 multiprocessing is a package ...

  7. 第六篇:python高级之网络编程

    python高级之网络编程   python高级之网络编程 本节内容 网络通信概念 socket编程 socket模块一些方法 聊天socket实现 远程执行命令及上传文件 socketserver及 ...

  8. Python系列之多线程、多进程

    线程是操作系统直接支持的执行单元,因此,高级语言通常都内置多线程的支持,Python也不例外,并且,Python的线程是真正的Posix Thread,而不是模拟出来的线程. Python的标准库提供 ...

  9. Python高级语法之:一篇文章了解yield与Generator生成器

    Python高级语法中,由一个yield关键词生成的generator生成器,是精髓中的精髓.它虽然比装饰器.魔法方法更难懂,但是它强大到我们难以想象的地步:小到简单的for loop循环,大到代替多 ...

随机推荐

  1. Python中三目计算符的正确用法及短路逻辑

    今天在看别人代码时看到这样一种写法, 感觉是个挺容易踩到的坑, 搞清楚后写出来备忘. 短路逻辑 Python中进行逻辑运算的时候, 默认采用的是一种叫做短路逻辑的运算规则. 名字是很形象的, 下面直接 ...

  2. 微信公众平台自动回复wechatlib.jar的生成及wechatlib解析

    微信公众平台出来有一段时日了,官方提供的自动回复的接口调用大致是这么些类型(text/image/location/link),每个项目都如此拷贝代码,在笔者看来比较麻烦,今天乘着点闲暇的时间特意将这 ...

  3. Entity Framework 中的Code First 中引入数据库函数

    1,在项目中添加CodeFirstStoreFunctions包: Install-Package EntityFramework.CodeFirstStoreFunctions 2,注册注册函数,F ...

  4. durex-word

    "(半夜没睡着) “你是不是饿了,哎呀我也饿了.”" "(聊到合拍处) “我和你有一万句me too想要说.”" "(异地恋) “我辞职,去你那儿吧! ...

  5. 移动页面div居中效果代码

    在线查看效果:http://hovertree.com/texiao/mobile/4.htm 可用手机浏览器查看 以下为HTML文件: <!DOCTYPE html> <html& ...

  6. MSSQL 分页

    使用数据库分页返回用户数据有如下好处:1.减少服务器磁盘系统地读取压力2.减少网络流量,减轻网络压力3.减轻客户端显示数据的压力4.提高处理效率. 一般而言分页处理分为两种:应用程序中的分页(查询出所 ...

  7. Objective-C 排序

    在Objective-C中,排序分为: 1.Foundation框架中的对象排序 2.自定义对象排序 例子:每个学生都有一个成绩score属性,根据成绩score对学生排序 自定义对象 Student ...

  8. VBoxManage命令

    查看有哪些虚拟机VBoxManage list vms 查看虚拟的详细信息VBoxManage list vms –long 查看运行着的虚拟机VBoxManage list runningvms 开 ...

  9. iOS -[PFPASIDataCompressor compressBytes:length:error:shouldFinish:] in PFPGZIPInvocationCompressor.o

    添加动态库报错 "_deflate", referenced from: -[PFPASIDataCompressor compressBytes:length:error:sho ...

  10. iOS开发之功能模块--长方形UIImage截取中间最大正方形区域

    这里直接用CoreGraphics的一些处理图片的方法,本身不难,但是有些时候用的不多,就会遗忘掉使用方法的细节.下面就直接展示关键源码,以便下次重复需求,就可以立马找回. 该方法中在UIImage的 ...