进程、线程

http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

使用threading模块实现多线程编程[综述]

Python这门解释性语言也有专门的线程模型,Python虚拟机使用GIL(Global Interpreter Lock,全局解释器锁)来互斥线程对共享资源的访问,但暂时无法利用多处理器的优势。

在Python中我们主要是通过thread和 threading这两个模块来实现的,其中Python的threading模块是对thread做了一些包装的,可以更加方便的被使用,所以我们使用 threading模块实现多线程编程。这篇文章我们主要来看看Python对多线程编程的支持。

在语言层面,Python对多线程提供了很好的支持,可以方便地支持创建线程、互斥锁、信号量、同步等特性。下面就是官网上介绍threading模块的基本资料及功能:

实现模块

thread:多线程的底层支持模块,一般不建议使用;

threading:对thread进行了封装,将一些线程的操作对象化。

threading模块

Thread 线程类,这是我们用的最多的一个类,你可以指定线程函数执行或者继承自它都可以实现子线程功能;

Timer与Thread类似,但要等待一段时间后才开始运行;

Lock 锁原语,这个我们可以对全局变量互斥时使用;

RLock 可重入锁,使单线程可以再次获得已经获得的锁;

Condition 条件变量,能让一个线程停下来,等待其他线程满足某个“条件”;

Event 通用的条件变量。多个线程可以等待某个事件发生,在事件发生后,所有的线程都被激活;

Semaphore为等待锁的线程提供一个类似“等候室”的结构;

BoundedSemaphore 与semaphore类似,但不允许超过初始值;

Queue:实现了多生产者(Producer)、多消费者(Consumer)的队列,支持锁原语,能够在多个线程之间提供很好的同步支持。

其中Thread类

是你主要的线程类,可以创建进程实例。该类提供的函数包括:

getName(self) 返回线程的名字

isAlive(self) 布尔标志,表示这个线程是否还在运行中

isDaemon(self) 返回线程的daemon标志

join(self, timeout=None) 程序挂起,直到线程结束,如果给出timeout,则最多阻塞timeout秒

run(self) 定义线程的功能函数

setDaemon(self, daemonic) 把线程的daemon标志设为daemonic

setName(self, name) 设置线程的名字

start(self) 开始线程执行

其中Queue提供的类

Queue队列

LifoQueue后入先出(LIFO)队列

PriorityQueue 优先队列

Python threading模块

  1. #!/usr/bin/env python
  2. #-*- coding:utf-8 -*-
  3. # Author:DCC
  4. import threading
  5. import time
  6. def run(n):
  7. print("task",n)
  8. time.sleep(2)
  9. t1 = threading.Thread(target=run,args=("t1",))
  10. t2 = threading.Thread(target=run,args=("t2",))
  11. t1.start()
  12. t2.start()
  13. print(t1.getName())
  14. print(t2.getName())
  1. #!/usr/bin/env python
  2. #-*- coding:utf-8 -*-
  3. # Author:DCC
  4. import threading
  5. import time
  6. class MyThread(threading.Thread):
  7. def __init__(self,n):
  8. super(MyThread,self).__init__()
  9. self.n = n
  10. def run(self):
  11. print("running task",self.n)
  12. time.sleep(3)
  13. t1 = MyThread("t1")
  14. t2 = MyThread("t2")
  15. if __name__ == '__main__':
  16. t1.start()
  17. t2.start()
  18. print(t1.getName())
  19. print(t2.getName())

Join & Daemon

一般情况下 主线程是不等待子线程是否执行完成的,只是触发一下,就不管了。

1、join ()方法:主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,才可以接着往下执行,那么在调用这个线程时可以使用被调用线程的join方法。
原型:join([timeout])
里面的参数时可选的,代表线程运行的最大时间,即如果超过这个时间,不管这个此线程有没有执行完毕都会被回收,然后主线程或函数都会接着执行的。

  1. import threading
  2. import time
  3. class MyThread(threading.Thread):
  4. def __init__(self,id):
  5. threading.Thread.__init__(self)
  6. self.id = id
  7. def run(self):
  8. x = 0
  9. time.sleep(10)
  10. print self.id
  11.  
  12. if __name__ == "__main__":
  13. t1=MyThread(999)
  14. t1.start()
  15. for i in range(5):
  16. print i
  17. #执行结果
  18. 0
  19. 1
  20. 2
  21. 3
  22. 4
  23. 999

机器上运行时,4和999之间,有明显的停顿。解释:线程t1 start后,主线程并没有等线程t1运行结束后再执行,而是先把5次循环打印执行完毕(打印到4),然后sleep(10)后,线程t1把传进去的999才打印出来。
现在,我们把join()方法加进去(其他代码不变),看看有什么不一样,例子:

  1. import threading
  2. import time
  3. class MyThread(threading.Thread):
  4. def __init__(self,id):
  5. threading.Thread.__init__(self)
  6. self.id = id
  7. def run(self):
  8. x = 0
  9. time.sleep(10)
  10. print self.id
  11.  
  12. if __name__ == "__main__":
  13. t1=MyThread(999)
  14. t1.start()
  15. t1.join()
  16. for i in range(5):
  17. print i
  18. #执行结果
  19. 999
  20. 0
  21. 1
  22. 2
  23. 3
  24. 4

2、setDaemon()方法。主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出.这就是setDaemon方法的含义,这基本和join是相反的。此外,还有个要特别注意的:必须在start() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起。
例子:就是设置子线程随主线程的结束而结束:

  1. #!/usr/bin/env python
  2. #-*- coding:utf-8 -*-
  3. # Author:DCC
  4. import threading
  5. import time
  6. def run(n):
  7. print("task",n)
  8. time.sleep(2)
  9. print("thread done...",n)
  10. start_time = time.time()
  11. t_objs = [] #存线程实例
  12. for i in range(50):
  13. t = threading.Thread(target=run,args=("t-%s"% i,))
  14. t.setDaemon(True) #把当前线程设置为守护线程
  15. t.start()
  16. t_objs.append(t) # 为了不阻塞后面线程的启动,不在这里join,先放到-个列表里
  17. # print(t.getName())
  18. #time.sleep(2)
  19. print(threading.active_count())
  20. # for i in t_objs: #循环线程实例列表,等待所有线程执行完毕
  21. # t.join()
  22. print(time.time()-start_time)

线程锁

CPU执行任务时,在线程之间是进行随机调度的,并且每个线程可能只执行n条代码后就转而执行另外一条线程。由于在一个进程中的多个线程之间是共享资源和数据的,这就容易造成资源抢夺或脏数据,于是就有了锁的概念,限制某一时刻只有一个线程能访问某个指定的数据。

未枷锁

  1. import threading
  2. import time
  3. NUM = 0
  4. def show():
  5. global NUM
  6. NUM += 1
  7. name = t.getName()
  8. time.sleep(1) # 注意,这行语句的位置很重要,必须在NUM被修改后,否则观察不到脏数据的现象。
  9. print(name, "执行完毕后,NUM的值为: ", NUM)
  10.  
  11. for i in range(10):
  12. t = threading.Thread(target=show)
  13. t.start()
  14. print('main thread stop')

LOCK锁

普通锁,也叫互斥锁,是独占的,同一时刻只有一个线程被放行。

  1. import time
  2. import threading
  3. NUM = 10
  4. def func(lock):
  5. global NUM
  6. lock.acquire() # 让锁开始起作用
  7. NUM -= 1
  8. time.sleep(1)
  9. print(NUM)
  10. lock.release() # 释放锁
  11. lock = threading.Lock() # 实例化一个锁对象
  12. for i in range(10):
  13. t = threading.Thread(target=func, args=(lock,)) # 记得把锁当作参数传递给func参数
  14. t.start()

RLock(递归锁)

说白了就是在一个大锁中还要再包含子锁

threading模块的Lock类,它不支持嵌套锁。RLcok类的用法和Lock一模一样,但它支持嵌套,因此我们一般直接使用RLcok类。

  1. import threading, time
  2.  
  3. def run1():
  4. print("grab the first part data")
  5. lock.acquire()
  6. global num
  7. num += 1
  8. lock.release()
  9. return num
  10. def run2():
  11. print("grab the second part data")
  12. lock.acquire()
  13. global num2
  14. num2 += 1
  15. lock.release()
  16. return num2
  17. def run3():
  18. lock.acquire()
  19. res = run1()
  20. print('--------between run1 and run2-----')
  21. res2 = run2()
  22. lock.release()
  23. print(res, res2)
  24. if __name__ == '__main__':
  25. num, num2 = 0, 0
  26. lock = threading.RLock()
  27. for i in range(10):
  28. t = threading.Thread(target=run3)
  29. t.start()
  30. while threading.active_count() != 1:
  31. print(threading.active_count())
  32. else:
  33. print('----all threads done---')
  34. print(num, num2)

时器(Timer)

定时器,指定n秒后执行某操作。很简单但很使用的东西。

  1. from threading import Timer
  2.  
  3. def hello():
  4.  
  5. print("hello, world")
  6.  
  7. t = Timer(1, hello) # 表示1秒后执行hello函数
  8.  
  9. t.start()

信号量(Semaphore)

这种锁允许一定数量的线程同时更改数据,它不是互斥锁。比如地铁安检,排队人很多,工作人员只允许一定数量的人进入安检区,其它的人继续排队。

互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。

  1. import time
  2. import threading
  3.  
  4. def run(n):
  5. semaphore.acquire()
  6. print("run the thread: %s" % n)
  7. time.sleep(1)
  8. semaphore.release()
  9.  
  10. num = 0
  11. semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行
  12. for i in range(20):
  13. t = threading.Thread(target=run, args=(i,))
  14. t.start()

事件(Event)

事件主要提供了三个方法 set、wait、clear。

事件机制:全局定义了一个“Flag”,如果“Flag”的值为False,那么当程序执行wait方法时就会阻塞,如果“Flag”值为True,那么wait方法时便不再阻塞。这种锁,类似交通红绿灯(默认是红灯),它属于在红灯的时候一次性阻挡所有线程,在绿灯的时候,一次性放行所有的排队中的线程。

  • clear:将“Flag”设置为False

  • set:将“Flag”设置为True

  1. !/usr/bin/env python
  2. #-*- coding:utf-8 -*-
  3. # Author:DCC
  4.  
  5. import time
  6. import threading
  7. import random
  8.  
  9. event = threading.Event()
  10.  
  11. def lighter():
  12. count = 0
  13. event.set() #先设置成绿灯
  14. while True:
  15. if count > 5 and count < 10:
  16. #改成红灯
  17. event.clear() #把标志位清除
  18. print("\033[41;1m red.....\033[0m")
  19. elif count > 10:
  20. event.set() #设置成路灯
  21. count = 0
  22. else:
  23. print("\033[42;1m green \033[0m")
  24. time.sleep(1)
  25. count +=1
  26. def car(name):
  27. while True:
  28. if event.is_set(): #代表绿灯
  29. print("[%s] is running " % name)
  30. time.sleep(2)
  31. else:
  32. print("[%s] is waitting....... " % name)
  33. event.wait()
  34. print("[%s] green light is on ,start going" % name)
  35.  
  36. light = threading.Thread(target=lighter,)
  37. light.start()
  38. car1 = threading.Thread(target=car,args=("Tesla",))
  39. car1.start()

队列

通常而言,队列是一种先进先出的数据结构,与之对应的是堆栈这种后进先出的结构。但是在python中,它内置了一个queue模块,它不但提供普通的队列,还提供一些特殊的队列

Queue:先进先出队列

  1. import queue
  2. q = queue.Queue(5)
  3. q.put(11)
  4. q.put(22)
  5. q.put(33)
  6.  
  7. print(q.get())
  8. print(q.get())
  9. print(q.get())

Queue类的参数和方法:

  • qsize() 获取当前队列中元素的个数,也就是队列的大小

  • empty() 判断当前队列是否为空,返回True或者False

  • full() 判断当前队列是否已满,返回True或者False

  • put(self, block=True, timeout=None)

  • get(self, block=True, timeout=None)

LifoQueue:后进先出队列

  1. import queue
  2. q = queue.LifoQueue()
  3. q.put(123)
  4. q.put(456)
  5. print(q.get())

PriorityQueue:优先级队列

  1. q = queue.PriorityQueue()
  2. q.put((1,"alex1"))
  3. q.put((1,"alex2"))
  4. q.put((1,"alex3"))
  5. q.put((3,"alex3"))
  6. print(q.get())

生产者消费者模型

  1. import time,random
  2.  
  3. import queue,threading
  4.  
  5. q = queue.Queue()
  6.  
  7. def Producer(name):
  8.  
  9. count = 0
  10.  
  11. while count <20:
  12.  
  13. time.sleep(random.randrange(3))
  14.  
  15. q.put(count)
  16.  
  17. print('Producer %s has produced %s baozi..' %(name, count))
  18.  
  19. count +=1
  20.  
  21. def Consumer(name):
  22.  
  23. count = 0
  24.  
  25. while count <20:
  26.  
  27. time.sleep(random.randrange(4))
  28.  
  29. if not q.empty():
  30.  
  31. data = q.get()
  32.  
  33. print(data)
  34.  
  35. print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))
  36.  
  37. else:
  38.  
  39. print("-----no baozi anymore----")
  40.  
  41. count +=1
  42.  
  43. p1 = threading.Thread(target=Producer, args=('A',))
  44.  
  45. c1 = threading.Thread(target=Consumer, args=('B',))
  46.  
  47. p1.start()
  48.  
  49. c1.start()

day9---多线程,线程锁,队列的更多相关文章

  1. Python多线程-线程锁

    多线程修改一份数据时需要用到线程锁,以防止数据修改出错 #-*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import threa ...

  2. c++11多线程---线程锁(mutex)

    #include<mutex> 包含四类锁: 1      std::mutex    最基本也是最常用的互斥类 2      std::recursive_mutex  同一线程内可递归 ...

  3. python_way ,day11 线程,怎么写一个多线程?,队列,生产者消费者模型,线程锁,缓存(memcache,redis)

    python11 1.多线程原理 2.怎么写一个多线程? 3.队列 4.生产者消费者模型 5.线程锁 6.缓存 memcache redis 多线程原理 def f1(arg) print(arg) ...

  4. python并发编程-多线程实现服务端并发-GIL全局解释器锁-验证python多线程是否有用-死锁-递归锁-信号量-Event事件-线程结合队列-03

    目录 结合多线程实现服务端并发(不用socketserver模块) 服务端代码 客户端代码 CIL全局解释器锁****** 可能被问到的两个判断 与普通互斥锁的区别 验证python的多线程是否有用需 ...

  5. Linux多线程系列-2-条件变量的使用(线程安全队列的实现)

    多线程情况下,往往需要使用互斥变量来实现线程间的同步,实现资源正确共享. linux下使用如下变量和函数 //条件变量 pthread_cond_t int pthread_cond_init (pt ...

  6. Erlang运行时中的无锁队列及其在异步线程中的应用

    本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过“线程进度”机制解决无锁队列的问题,并介绍 Erlang ...

  7. 线程、进程、daemon、GIL锁、线程锁、递归锁、信号量、计时器、事件、队列、多进程

    # 本文代码基于Python3 什么是进程? 程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是进程运行 ...

  8. 进击的Python【第九章】:paramiko模块、线程与进程、各种线程锁、queue队列、生产者消费者模型

    一.paramiko模块 他是什么东西? paramiko模块是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接. 先来个实例: import param ...

  9. Java多线程面试题:线程锁+线程池+线程同步等

    1.并发编程三要素? 1)原子性 原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行. 2)可见性 可见性指多个线程操作一个共享变量时,其中一个线程对变量 ...

随机推荐

  1. [刘阳Java]_MyBatis_映射文件的resultMap标签入门_第4讲

    <resultMap>:用于解决实体类中属性和表字段名不相同的问题 id:表示当前<resultMap>标签的唯一标识 result:定义表字段和实体类属性的对应关系 prop ...

  2. 苗子sale record

    2016年7月11日 橘白/阳光 发广州 220  中通 719373064939 成功2016年7月11日 横纹RA    发潮州 250  中通 719373064940 成功2016年7月18日 ...

  3. 前端工作面试问题--摘取自github

    前端工作面试问题 本文包含了一些用于考查候选者的前端面试问题.不建议对单个候选者问及每个问题 (那需要好几个小时).只要从列表里挑选一些,就能帮助你考查候选者是否具备所需要的技能. 备注: 这些问题中 ...

  4. NOIp蒟蒻的爆零记——HA-0132

    考前: 从十一月开始的听课集训,连考六场:考前的最后两天写(da)着(zhe)各种各样的奇(C)葩(S)模板:一周的疯狂,已经过去: 考前的一晚:第二批高二的六个人聚在一起(还有滑稽大师),愉快的玩( ...

  5. MAVEN解决Cannot change version of project facet Dynamic web module to 2.5

    我们用Eclipse创建Maven结构的web项目的时候选择了Artifact Id为maven-artchetype-webapp,由于这个catalog比较老,用的servlet还是2.3的,而一 ...

  6. Linux 配置nginx

    1.首先安装依赖包: # yum -y install gcc gcc-c++ make libtool zlib zlib-devel openssl openssl-devel pcre pcre ...

  7. [16]APUE:套接字

    [a] socket / socketpair #include <sys/socket.h> int socket(int domain, int type, int protocol) ...

  8. 第四章 使用Docker镜像和仓库(二)

    第四章 使用Docker镜像和仓库(二) 回顾: 开始学习之前,我先pull下来ubuntu和fedora镜像 [#9#cloudsoar@cloudsoar-virtual-machine ~]$s ...

  9. Xamarin For Android 遇到错误java.exe exited with code 1. (msb6006)

    今天用Xamarin编译一个android工程遇到这样一个问题:java.exe exited with code 1. (msb6006),项目代码没有问题.于是各种谷歌 ,在http://foru ...

  10. webrtc进阶-信令篇-之三:信令、stun、turn、ice

    webRTC支持点对点通讯,但是webRTC仍然需要服务端:  . 协调通讯过程中客户端之间需要交换元数据,    如一个客户端找到另一个客户端以及通知另一个客户端开始通讯.  . 需要处理NAT(网 ...