Python 与线程

线程是进程的执行单元,对于大多数程序来说,可能只有一个主线程,但是为了能够提高效率,有些程序会采用多线程,在系统中所有的线程看起来都是同时执行的,例如,现在的多线程网络下载程序中,就使用了这种线程并发的特性,程序将欲下载的文件分成多个部分,然后同时进行下载,从而加快速度.虽然线程并不是一个容易掌握和使用的概念,但是如果运用得当,还是可以获得很不错的性能的.

◆创建使用线程◆

在 Python 中创建线程需要用到一个类,threading类,其类的实现方法是底层调用了C语言的原生函数来实现的创建线程,创建线程有两种方式,一种是直接使用函数创建线程,另一种则是使用类创建线程,两种创建方式效果是相同的,但是需要注意一点,在使用类的方式创建线程的时候,默认执行run(self)方法,且此函数名称必须是run不能修改,接下来看3个小例子吧.

使用函数创建线程: 通过线程模块创建线程,并传递参数即可实现直接对指定函数实现多线程.

  1. import os
  2. import threading
  3. import time
  4. def MyThread(x,y): #定义每个线程要执行的函数体
  5. print("传递的数据:%s,%s"%(x,y)) #其中有两个参数,我们动态传入
  6. time.sleep(5) #睡眠5秒钟
  7. for x in range(10): #创建10个线程并发执行函数
  8. thread = threading.Thread(target=MyThread,args=(x,x+1,)) #args是函数的参数,元组最后一个必须要逗号.
  9. thread.start() #启动线程

使用类创建线程: 通过定义类,传递给类中一些参数,然后启动多线程,这种方式不常用.

  1. import os
  2. import threading
  3. import time
  4. class MyThread(threading.Thread): #继承threading.Thread类
  5. def __init__(self,x,y): #重写构造函数
  6. super(MyThread,self).__init__() #先执行父类的构造方法
  7. self.x = x
  8. self.y = y
  9. def run(self): #run()方法,是cpu调度线程会使用的方法,名称必须是run
  10. print("运行线程, X=%s Y=%s"%(self.x,self.y))
  11. for i in range(10): #创建10个线程
  12. obj = MyThread(i,i+10)
  13. obj.start()
  1. import paramiko,datetime,threading
  2. class MyThread(threading.Thread):
  3. def __init__(self,address,username,password,port,command):
  4. super(MyThread, self).__init__()
  5. self.address = address
  6. self.username = username
  7. self.password = password
  8. self.port = port
  9. self.command = command
  10. def run(self):
  11. ssh = paramiko.SSHClient()
  12. ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  13. try:
  14. ssh.connect(self.address, port=self.port, username=self.username, password=self.password, timeout=1)
  15. stdin, stdout, stderr = ssh.exec_command(self.command)
  16. result = stdout.read()
  17. if not result:
  18. self.result = stderr.read()
  19. ssh.close()
  20. self.result = result.decode()
  21. except Exception:
  22. self.result = "0"
  23. def get_result(self):
  24. try:
  25. return self.result
  26. except Exception:
  27. return None
  28. ThreadPool = [] # 定义线程池
  29. starttime = datetime.datetime.now()
  30. for item in range(5):
  31. obj = MyThread("192.168.1.20","root","123","22","ifconfig")
  32. ThreadPool.append(obj)
  33. for item in ThreadPool:
  34. item.start() # 启动线程
  35. item.join()
  36. for item in ThreadPool:
  37. ret = item.get_result() # 获取返回结果
  38. print(ret)
  39. endtime = datetime.datetime.now()
  40. print("程序开始运行:{} 结束:{}".format(starttime,endtime))

接收线程返回结果: 我们可以使用join方法,等待线程执行完毕后的返回结果.

  1. import os
  2. import threading
  3. import time
  4. def MyThread(x,y): #定义每个线程要执行的函数体
  5. print("传递的数据:%s,%s"%(x,y)) #其中有两个参数,我们动态传入
  6. time.sleep(5) #睡眠5秒钟
  7. return "ok"
  8. temp=[]
  9. for x in range(10): #创建10个线程并发执行函数
  10. thread = threading.Thread(target=MyThread,args=(x,x+1,)) #args是函数的参数,元组最后一个必须要逗号.
  11. thread.start() #启动线程
  12. temp.append(thread) #将线程结果添加到列表
  13. for y in temp: #遍历这个线程列表
  14. #此处一定要join,不然主线程比子线程跑的快,会拿不到结果,程序就退出执行了.
  15. y.join() #等待线程执行完毕,返回结果
  16. print("线程: %s"%y)

◆线程锁与信号◆

由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以就出现了线程锁的概念,即在同一时刻只允许一个线程执行操作,在这里我们选择使用Rlock,而不使用Lock,因为Lock如果多次获取锁的时候会出错,而RLock允许在同一线程中被多次acquire,但是需要用n次的release才能真正释放所占用的琐,一个线程获取了锁在释放之前,其他线程只有等待线程结束后在进行操作.

全局锁(Lock): 添加本全局锁以后,能够保证在同一时间内保证只有一个线程具有权限.

  1. import time
  2. import threading
  3. num = 0 #定义全局共享变量
  4. thread_list = [] #线程列表
  5. lock = threading.Lock() #生成全局锁
  6. def SumNumber():
  7. global num #在每个线程中获取这个全局变量
  8. time.sleep(2)
  9. lock.acquire() #修改数据前给数据加锁
  10. num += 1 #每次进行递增操作
  11. lock.release() #执行完毕以后,解除锁定
  12. for x in range(50): #指定生成线程数
  13. thread = threading.Thread(target=SumNumber)
  14. thread.start() #启动线程
  15. thread_list.append(thread) #将结果列表加入到变量中
  16. for y in thread_list: #等待执行完毕.
  17. y.join()
  18. print("计算结果: ",num)

递归锁(RLock): 递归锁和全局锁差不多,递归锁就是在大锁中还要添加个小锁,递归锁是常用的锁.

  1. import threading
  2. import time
  3. num = 0 #初始化全局变量
  4. lock = threading.RLock() #设置递归锁
  5. def fun1():
  6. lock.acquire() #添加递归锁
  7. global num
  8. num += 1
  9. lock.release() #关闭递归锁
  10. return num
  11. def fun2():
  12. lock.acquire() #添加递归锁
  13. res = fun1()
  14. print("计算结果: ",res)
  15. lock.release() #关闭递归锁
  16. if __name__ == "__main__":
  17. for x in range(10): #生成10个线程
  18. thread = threading.Thread(target=fun2)
  19. thread.start()
  20. while threading.active_count() != 1: #等待所有线程执行完成
  21. print(threading.active_count())
  22. else:
  23. print("所有线程运行完成...")
  24. print(num)

互斥锁(Semaphore): 同时允许一定数量的线程更改数据,也就是限制每次允许执行的线程数.

  1. import threading,time
  2. num = 0 #初始化变量
  3. semaphore = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
  4. def run(n):
  5. semaphore.acquire() #添加信号
  6. time.sleep(1)
  7. print("运行这个线程中: %s"%n)
  8. semaphore.release() #关闭信号
  9. if __name__ == '__main__':
  10. for i in range(20): #同时执行20个线程
  11. t = threading.Thread(target=run, args=(i,))
  12. t.start()
  13. while threading.active_count() != 1: #等待所有线程执行完毕
  14. pass
  15. else:
  16. print('----所有线程执行完毕了---')
  17. print(num)

◆线程驱动事件◆

事件驱动(Event): 线程事件用于主线程控制其他线程的执行,事件主要提供了三个方法set、wait、clear、is_set,分别用于设置检测和清除标志.

事件处理机制定义:全局定义了一个"Flag",如果"Flag"值为False,那么当程序执行event.wait 方法时就会阻塞,如果"Flag"值为True,那么在执行event.wait 方法时便不再阻塞,变成可执行模式,总体来说需要了解以下四个方法.

clear:将"Flag"设置为False

set:将"Flag"设置为True

wait:检测当前"Flag",如果"Flag"值为 False,那么当线程执行 event.wait 方法时就会阻塞,如果"Flag"值为True,那么event.wait 方法时便不再阻塞

is_set:检测当前的状态,是否阻塞

  1. import threading
  2. event = threading.Event()
  3. def func(x,event):
  4. print("函数被执行了: %s 次.." %x)
  5. event.wait() #检测标志位状态,如果为True=继续执行以下代码,反之等待.
  6. print("加载执行结果: %s" %x)
  7. for i in range(10): #创建10个线程
  8. thread = threading.Thread(target=func,args=(i,event,))
  9. thread.start()
  10. print("当前状态: %s" %event.is_set()) #检测当前状态,这里为False
  11. event.clear() #将标志位设置为False,默认为False
  12. temp=input("输入yes: ") #输入yes手动设置为True
  13. if temp == "yes":
  14. event.set() #设置成True
  15. print("当前状态: %s" %event.is_set()) #检测当前状态,这里为True

定时器(Timer): 指定定时器,作用是让进程或者是指定函数,在n秒后执行相应的操作.

  1. import threading
  2. import time
  3. def func():
  4. print("hello python")
  5. for i in range(5): #指定5个线程
  6. thread = threading.Timer(5,func) #在5秒钟以后运行func函数
  7. thread.start()

## Python 与进程

直观地说,进程就是正在执行的程序,进程是多任务操作系统中执行任务的基本单元,是包含了程序指令和相关资源的集合,线程的上一级就是进程,进程可包含很多线程,进程和线程的区别是进程间的数据不共享,多进程也可以用来处理多任务,不过多进程很消耗资源,计算型的任务最好交给多进程来处理,IO密集型最好交给多线程来处理,此外进程的数量应该和cpu的核心数保持一致. 

进程与线程的区别,有以下几种解释:

● 新创建一个线程很容易,新创建一个进程需要复制父进程

● 线程共享创建它的进程的地址空间,进程有自己的地址空间

● 主线程可以控制相当大的线程在同一进程中,进程只能控制子进程

● 线程是直接可以访问线程之间的数据,进程需要复制父进程的数据才能访问

● 主线程变更可能会影响进程的其他线程的行为,父进程的变化不会影响子进程

● 线程可以直接与其他线程的通信过程,进程必须使用进程间通信和同胞交流过程

◆创建一个进程◆

通常情况下,创建一个进程需要使用multiprocessing 模块,具体的创建方法和上面的线程创建方法相同,唯一的不同是关键字的变化,但需要注意的是,由于进程之间的数据需要各自持有一份,所以创建进程需要的非常大的开销,其他使用方法和线程threading.Thread是一样的,如下介绍两个创建进程例子.  

创建进程(1): 通过使用multiprocessing库,循环创建5个进程,并使用join等待进程执行完毕.

  1. import multiprocessing
  2. import time
  3. def func(name):
  4. time.sleep(2)
  5. print("hello",name)
  6. if __name__ == "__main__":
  7. for i in range(5):
  8. proc = multiprocessing.Process(target=func,args=("lyshark",))
  9. proc.start()
  10. proc.join()

创建进程(2): 创建5个进程,并在每个进程里启动1个线程,线程打印出线程的ID号.

  1. import multiprocessing
  2. import threading
  3. import time
  4. def thread_run():
  5. print("子线程->子线程ID: %s" %threading.get_ident())
  6. def func(num):
  7. time.sleep(2)
  8. print("-------------------------------->>> 主线程->主线程ID %s" %num)
  9. for i in range(5): #在主线程里开辟5个子线程
  10. thread = threading.Thread(target=thread_run,) #嵌套一个子线程
  11. thread.start() #执行子线程
  12. if __name__ == "__main__":
  13. for i in range(5): #启动5个主线程
  14. proc = multiprocessing.Process(target=func,args=(i,))
  15. proc.start()
  16. #proc.join()

◆进程数据共享◆

一般当我们创建两个进程后,进程各自持有一份数据,默认无法共享数据,如果我们想要共享数据必须通过一个中间件来实现数据的交换,来帮你把数据进行一个投递,要实现进程之间的数据共享,其主要有以下几个方法来实现进程间数据的共享,queues,Array,Manager.dict,pipe这些方法都能实现数据共享,下面将举几个小例子进行说明.

共享队列(Queue): 这个Queue主要实现进程与进程之间的数据共享,与线程中的Queue不同.

  1. from multiprocessing import Process
  2. from multiprocessing import queues
  3. import multiprocessing
  4. def foo(i,arg):
  5. arg.put(i)
  6. print('say hi',i,arg.qsize())
  7. li = queues.Queue(20,ctx=multiprocessing)
  8. for i in range(10):
  9. p = Process(target=foo,args=(i,li,))
  10. p.start()

共享整数(int): 整数之间的共享,只需要使用multiprocessing.Value方法,即可实现.

  1. import multiprocessing
  2. def func(num):
  3. num.value = 1024 #虽然赋值了,但是子进程改变了这个数值
  4. print("函数中的数值: %s"%num.value)
  5. if __name__ == "__main__":
  6. num = multiprocessing.Value("d",10.0) #主进程与子进程共享这个value
  7. print("这个共享数值: %s"%num.value)
  8. for i in range(5):
  9. num = multiprocessing.Value("d", i) #声明进程,并传递1,2,3,4这几个数
  10. proc = multiprocessing.Process(target=func,args=(num,))
  11. proc.start() #启动进程
  12. #proc.join()
  13. print("最后打印数值: %s"%num.value)

共享数组(Array): 数组之间的共享,只需要使用multiprocessing.Array方法,即可实现.

  1. import multiprocessing
  2. def func(ary): #子进程改变数组,主进程跟着改变
  3. ary[0]=100
  4. ary[1]=200
  5. ary[2]=300
  6. ''' i所对应的类型是ctypes.c_int,其他类型如下参考:
  7. 'c': ctypes.c_char, 'u': ctypes.c_wchar,
  8. 'b': ctypes.c_byte, 'B': ctypes.c_ubyte,
  9. 'h': ctypes.c_short, 'H': ctypes.c_ushort,
  10. 'i': ctypes.c_int, 'I': ctypes.c_uint,
  11. 'l': ctypes.c_long, 'L': ctypes.c_ulong,
  12. 'f': ctypes.c_float, 'd': ctypes.c_double
  13. '''
  14. if __name__ == "__main__":
  15. ary = multiprocessing.Array("i",[1,2,3]) #主进程与子进程共享这个数组
  16. for i in range(5):
  17. proc = multiprocessing.Process(target=func,args=(ary,))
  18. print(ary[:])
  19. proc.start()

共享字典(dict): 通过使用Manager()方法,实现两个进程中的,字典与列表的数据共享.

  1. import multiprocessing
  2. def func(mydict, mylist):
  3. mydict["字典1"] = "值1"
  4. mydict["字典2"] = "值2"
  5. mylist.append(1)
  6. mylist.append(2)
  7. mylist.append(3)
  8. if __name__ == "__main__":
  9. mydict = multiprocessing.Manager().dict() #主进程与子进程共享字典
  10. mylist = multiprocessing.Manager().list() #主进程与子进程共享列表
  11. proc = multiprocessing.Process(target=func,args=(mydict,mylist))
  12. proc.start()
  13. proc.join()
  14. print("列表中的元素: %s" %mylist)
  15. print("字典中的元素: %s" %mydict)

管道共享(Pipe): 通过Pipe管道的方式在两个进程之间共享数据,类似于Socket套接字.

  1. import multiprocessing
  2. def func(conn):
  3. conn.send("你好我是子进程.") #发送消息给父进程
  4. print("父进程传来了:",conn.recv()) #接收父进程传来的消息
  5. conn.close()
  6. if __name__ == "__main__":
  7. parent_conn,child_conn = multiprocessing.Pipe() #管道创建两个端口,一收一发送
  8. proc = multiprocessing.Process(target=func,args=(child_conn,))
  9. proc.start()
  10. print("子进程传来了:",parent_conn.recv()) #接收子进程传来的数据
  11. parent_conn.send("我是父进程,收到消息了..") #父进程发送消息给子进程

进程锁(Lock): 进程中也有锁,可以实现进程之间数据的一致性,也就是进程数据的同步,保证数据不混乱.

  1. import multiprocessing
  2. def func(loc,num):
  3. loc.acquire() #添加进程锁
  4. print("hello ---> %s" %num)
  5. loc.release() #关闭进程锁
  6. if __name__ == "__main__":
  7. lock = multiprocessing.Lock() #生成进程锁
  8. for number in range(10):
  9. proc = multiprocessing.Process(target=func,args=(lock,number,))
  10. proc.start()

◆进程的进程池◆

进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止,进程池有两个方法:apply(),apply_async(),下面将介绍几个常用的小技巧.

进程池(apply): 同步执行,每次执行一个进程,直到所有进程执行完毕,其实也就是串行执行.

  1. import multiprocessing
  2. import time
  3. def foo(num):
  4. time.sleep(2)
  5. print("进程执行-->: %s"%num)
  6. if __name__ == "__main__":
  7. pool = multiprocessing.Pool(processes=5) #允许进程池同时放入5个进程
  8. for i in range(10):
  9. pool.apply(func=foo,args=(i,)) #并行执行每次执行一个
  10. print("ends ...")
  11. pool.close()
  12. pool.join()

进程池(apply_async): 异步执行进程,每次执行5个进程,直到执行完10次循环位置,并行执行.

  1. import multiprocessing
  2. import time
  3. def foo(num):
  4. time.sleep(2)
  5. print("进程执行-->: %s"%num)
  6. def bar(arg):
  7. print("call back 函数执行..")
  8. if __name__ == "__main__":
  9. pool = multiprocessing.Pool(processes=5) #允许进程池同时放入5个进程
  10. for i in range(10):
  11. pool.apply_async(func=foo,args=(i,),callback=bar) #每次执行进程结束,自动执行callback指定的函数
  12. print("ends ...")
  13. pool.close()
  14. pool.join()

## Python 与协程

协程,又称微线程,是一种用户态的轻量级线程,携程主要实现了在单线程下实现并发,一个线程能够被分割成多个协程,协程拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,因此协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态.

线程和进程的操作是由程序触发系统接口,最后的执行者是系统,协程的操作则是程序员,协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时,而协程则只使用一个线程,在一个线程中规定某个代码块执行顺序,协程的适用场景:当程序中存在大量不需要CPU的操作时(IO操作),时适用于协程.

协程之(Yield): 通过使用yield方法来模拟实现协程操作的例子,这里只是演示.

  1. import time
  2. import queue
  3. def consumer(name):
  4. print("--->包子...")
  5. while True:
  6. new_yield = yield
  7. print("[%s] 在吃包子 %s" % (name, new_yield))
  8. def producer():
  9. r = con.__next__()
  10. r = con2.__next__()
  11. n = 0
  12. while n < 5:
  13. n += 1
  14. con.send(n)
  15. con2.send(n)
  16. print("\033[32;1m[producer]\033[0m 生产包子.. %s" % n)
  17. if __name__ == '__main__':
  18. con = consumer("admin")
  19. con2 = consumer("lyshark")
  20. p = producer()

协程之(Greenlet): Greenlet协程模块,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator,但是仍然需要手动切换.

  1. from greenlet import greenlet
  2. def master():
  3. print("主程序执行...")
  4. green2.switch() #切换到slaves函数
  5. print("主程序再次执行...")
  6. green2.switch() #切换到master函数
  7. def slaves():
  8. print("子程序执行....")
  9. green1.switch() #切换到master函数
  10. print("子程序再次执行...")
  11. green1 = greenlet(master) #启动一个协程
  12. green2 = greenlet(slaves) #启动一个协程
  13. green1.switch() #切换到master函数

协程之(Gevent): Gevent是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,它是以C扩展模块形式接入Python的轻量级协程.

  1. import gevent
  2. def func1():
  3. print("函数 func1 开始...")
  4. gevent.sleep(3)
  5. print("函数 func1 结束...")
  6. def func2():
  7. print("函数 func2 开始...")
  8. gevent.sleep(1)
  9. print("函数 func2 结束...")
  10. def func3():
  11. print("函数 func3 开始...")
  12. gevent.sleep(0)
  13. print("函数 func3 结束...")
  14. gevent.joinall([
  15. gevent.spawn(func1), #切换协程
  16. gevent.spawn(func2),
  17. gevent.spawn(func3),
  18. ])

协程实现爬虫: 通过使用Gevent模块,实现批量爬取指定页面并返回页面的大小.

  1. from gevent import monkey
  2. monkey.patch_all() #把当前程序所有IO操作给我单独做上标记,打补丁
  3. import gevent
  4. from urllib.request import urlopen
  5. def func(url):
  6. print("获取页面: %s" %url)
  7. resp = urlopen(url)
  8. data = resp.read()
  9. print("%s URL大小为= %d bytes" %(url,len(data)))
  10. gevent.joinall([
  11. gevent.spawn(func, 'https://www.python.org/'),
  12. gevent.spawn(func, 'https://www.yahoo.com/'),
  13. gevent.spawn(func, 'https://github.com/'),
  14. ])

并发Socket(服务端): 在单线程下实现多Socket并发,服务端代码如下.

  1. import sys
  2. import socket
  3. import time
  4. import gevent
  5. from gevent import socket,monkey
  6. monkey.patch_all()
  7. def server(port):
  8. s = socket.socket()
  9. s.bind(('0.0.0.0', port))
  10. s.listen(500)
  11. while True:
  12. cli,addr = s.accept()
  13. gevent.spawn(handle_request,cli)
  14. def handle_request(conn):
  15. try:
  16. while True:
  17. data = conn.recv(1024)
  18. print("接收数据:", data)
  19. conn.send(data)
  20. if not data:
  21. conn.shutdown(socket.SHUT_WR)
  22. except Exception as ex:
  23. print(ex)
  24. finally:
  25. conn.close()
  26. if __name__ == '__main__':
  27. server(8001)

并发Socket(客户端): 在单线程下实现多Socket并发,客户端代码如下.

  1. import socket
  2. HOST = 'localhost'
  3. PORT = 8001
  4. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  5. s.connect((HOST, PORT))
  6. while True:
  7. msg = bytes(input("输入发送的数据:"), encoding="utf8")
  8. s.sendall(msg)
  9. data = s.recv(1024)
  10. print('返回数据', repr(data))
  11. s.close()

## Python 与队列

同步队列 Queue 这是一个专门为多线程访问所设计的数据结构,能够有效地实现线程对资源的访问,程序可以通过此结构在线程间安全有效地传递数据 Queue 模块中包含一个 Queue 的类,其构造函数中可以指定一个Maxsize值,当maxszie值小于或等于0的时候,表示对队列的长度没有限制,当大于0的时候,则指定了队列的长度.当队列到达最大长度而又有新的线程过来的时候,则需要等待 Queue 类中有不少方法,但是最市要的是 put 和 get 方法,Put 方法将需要完成的任务放入队列,而 get 方法相反,从队列中获取任务,需要注意的是,在这些方法中,有些方法由于多线程的原因,返回值并不一定是准确的,例如qsize,empty等函数的统计结果.

先进先出队列: 先来介绍简单的队列例子,以及队列的常用方法的使用,此队列是先进先出模式.

  1. import queue
  2. q = queue.Queue(5) #默认maxsize=0无限接收,最大支持的个数
  3. print(q.empty()) #查看队列是否为空,如果为空则返回True
  4. q.put(1) #PUT方法是,向队列中添加数据
  5. q.put(2) #第二个PUT,第二次向队列中添加数据
  6. q.put(3,block=False,timeout=2) #是否阻塞:默认是阻塞block=True,timeout=超时时间
  7. print(q.full()) #查看队列是否已经放满
  8. print(q.qsize()) #队列中有多少个元素
  9. print(q.maxsize) #队列最大支持的个数
  10. print(q.get(block=False,timeout=2)) #GET取数据
  11. print(q.get())
  12. q.task_done() #join配合task_done,队列中有任务就会阻塞进程,当队列中的任务执行完毕之后,不在阻塞
  13. print(q.get())
  14. q.task_done()
  15. q.join() #队列中还有元素的话,程序就不会结束程序,只有元素被取完配合task_done执行,程序才会结束

后进先出队列: 这个队列则是,后进先出,也就是最后放入的数据最先弹出,类似于堆栈.

  1. >>> import queue
  2. >>>
  3. >>> q = queue.LifoQueue()
  4. >>>
  5. >>> q.put("wang")
  6. >>> q.put("rui")
  7. >>> q.put("ni")
  8. >>> q.put("hao")
  9. >>>
  10. >>> print(q.get())
  11. hao
  12. >>> print(q.get())
  13. ni
  14. >>> print(q.get())
  15. rui
  16. >>> print(q.get())
  17. wang
  18. >>> print(q.get())

优先级队列: 此类队列,可以指定优先级顺序,默认从高到低排列,以此根据优先级弹出数据.

  1. >>> import queue
  2. >>>
  3. >>> q = queue.PriorityQueue()
  4. >>>
  5. >>> q.put((1,"python1"))
  6. >>> q.put((-1,"python2"))
  7. >>> q.put((10,"python3"))
  8. >>> q.put((4,"python4"))
  9. >>> q.put((98,"python5"))
  10. >>>
  11. >>> print(q.get())
  12. (-1, 'python2')
  13. >>> print(q.get())
  14. (1, 'python1')
  15. >>> print(q.get())
  16. (4, 'python4')
  17. >>> print(q.get())
  18. (10, 'python3')
  19. >>> print(q.get())
  20. (98, 'python5')

双向的队列: 双向队列,也就是说可以分别从两边弹出数据,没有任何限制.

  1. >>> import queue
  2. >>>
  3. >>> q = queue.deque()
  4. >>>
  5. >>> q.append(1)
  6. >>> q.append(2)
  7. >>> q.append(3)
  8. >>> q.append(4)
  9. >>> q.append(5)
  10. >>>
  11. >>> q.appendleft(6)
  12. >>>
  13. >>> print(q.pop())
  14. 5
  15. >>> print(q.pop())
  16. 4
  17. >>> print(q.popleft())
  18. 6
  19. >>> print(q.popleft())
  20. 1
  21. >>> print(q.popleft())
  22. 2

生产者消费者模型: 生产者消费者模型,是各种开发场景中最常用的开发模式,以下是模拟的模型.

  1. import queue
  2. import threading
  3. import time
  4. q = queue.Queue()
  5. def productor(arg):
  6. while True:
  7. q.put(str(arg))
  8. print("%s 号窗口有票...."%str(arg))
  9. time.sleep(1)
  10. def consumer(arg):
  11. while True:
  12. print("第 %s 人取 %s 号窗口票"%(str(arg),q.get()))
  13. time.sleep(1)
  14. for i in range(10): #负责生产票数
  15. t = threading.Thread(target=productor,args=(i,))
  16. t.start()
  17. for j in range(5): #负责取票,两个用户取票
  18. t = threading.Thread(target=consumer,args=(j,))
  19. t1 = threading.Thread(target=consumer,args=(j,))
  20. t.start()
  21. t1.start()

Python 线程&进程与协程的更多相关文章

  1. 多任务-python实现-进程,协程,线程总结(2.1.16)

    @ 目录 1.类比 2.总结 关于作者 1.类比 一个生产玩具的工厂: 一个生产线成为一个进程,一个生产线有多个工人,所以工人为线程 单进程-多线程:一条生产线,多个工人 多进程-多线程:多条生产线, ...

  2. Python之线程、进程和协程

    python之线程.进程和协程 目录: 引言 一.线程 1.1 普通的多线程 1.2 自定义线程类 1.3 线程锁 1.3.1 未使用锁 1.3.2 普通锁Lock和RLock 1.3.3 信号量(S ...

  3. python成长之路 :线程、进程和协程

    python线程 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分 ...

  4. Python之路【第七篇】:线程、进程和协程

    Python之路[第七篇]:线程.进程和协程   Python线程 Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元. 1 2 3 4 5 6 7 8 9 10 11 12 1 ...

  5. python运维开发(十一)----线程、进程、协程

    内容目录: 线程 基本使用 线程锁 自定义线程池 进程 基本使用 进程锁 进程数据共享 进程池 协程 线程 线程使用的两种方式,一种为我们直接调用thread模块上的方法,另一种我们自定义方式 方式一 ...

  6. Python 线程和进程和协程总结

    Python 线程和进程和协程总结 线程和进程和协程 进程 进程是程序执行时的一个实例,是担当分配系统资源(CPU时间.内存等)的基本单位: 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其 ...

  7. python 线程 进程 协程 学习

    转载自大神博客:http://www.cnblogs.com/aylin/p/5601969.html 仅供学习使用···· python 线程与进程简介 进程与线程的历史 我们都知道计算机是由硬件和 ...

  8. python并发编程之Queue线程、进程、协程通信(五)

    单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...

  9. Python菜鸟之路:Python基础-线程、进程、协程

    上节内容,简单的介绍了线程和进程,并且介绍了Python中的GIL机制.本节详细介绍线程.进程以及协程的概念及实现. 线程 基本使用 方法1: 创建一个threading.Thread对象,在它的初始 ...

随机推荐

  1. static关键字的用法小结

    static:是一个修饰符,用于修饰成员(成员变量,成员函数). 当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,还可以直接被类名调用,写法:类名.静态成员 static特点: 1.随着 ...

  2. C语言问答九问

    C语言问题观: 一.环境搭建问题:linux,(可以是w10下linux子系统):vim(notepad++),gcc编译器,gdb调试器,make自动编译工具,ddd分析工具和valgrind分析工 ...

  3. elasticsearch _source

    默认地,Elasticsearch 在 _source 字段存储代表文档体的JSON字符串.和所有被存储的字段一样, _source 字段在被写入磁盘之前先会被压缩.这个字段的存储几乎总是我们想要的, ...

  4. Linux设备驱动程序 之 中断下半部

    中断处理程序的局限 1. 中断处理程序以异步的方式执行,并且它有可能会打断其他重要代码的执行,因此,为了避免被打段的代码停止时间过长,中断处理程序应该执行的越快越好: 2. 如果当前有一个中断处理程序 ...

  5. win10系统在执行“ vagrant box add centos7 vagrant-centos-7.box”添加box时,报错“Vagrant failed to initialize at a very early stage: Failed to locate the powershell executable on the available PATH. ”

    这个意思是:在有效的路径中未能执行PowerShell命令. 请检查PowerShell的安装和有效的路径,然后再尝试重新运行这个命令. 在环境变量path中添加powershell的路径,例如:C: ...

  6. Mac下持续集成-jenkins设置密码及启动

    什么情况呢,现在想起来重新启动jenkins时发现,一切都要从头开始... 输入原始密码: 提示密码在:/var/root/.jenkins/secrets/initialAdminPassword ...

  7. Ubuntu 18.04安装arm-linux-gcc交叉编译器(超简单,附安装包下载地址)

    目前网上搜索发现,最多人安装的是4.4.3版本的: arm-linux-gcc-4.4.3.tar.gz下载地址:https://pan.baidu.com/s/1rAIBASIRZAXl-P1UOW ...

  8. P3015 [USACO11FEB]最好的括号Best Parenthesis

    P3015 [USACO11FEB]最好的括号Best Parenthesis 题解 一定要开 long long !!! 通过阅读英文题面我们知道所给出的字符串是已经匹配好的,所以我们只是计算就好了 ...

  9. ZXHN H218N 超级管理员账号

    telecomadmin nE7jA%5m cmcc空格cmcc空格cmccaDm8H%MdA

  10. prometheus监控插件mysqld_exporter

    1,首先需要增加授权 mysql> GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost' IDEN ...