来源:田飞雨

链接:http://www.jianshu.com/p/12cd213a93bf

虽然python中由于GIL的机制致使多线程不能利用机器多核的特性,但是多线程对于我们理解并发模型以及底层操作非常有用。

线程的有两种使用方法,一种是在函数使用,一种是放在类中使用。

1,在函数中使用多线程

语法如下:

thread.start_new_thread(function, args[, kwargs] )

参数说明:

function - 线程函数。

args - 传递给线程函数的参数,必须是个tuple类型。

kwargs - 可选参数。

下面是一个例子:

def run(num):

print 'hi , i am a thread.', num

def main():

threads = []

for i in range(5):

t = threading.Thread(target=run, args=(i,))

threads.append(t)

t.start()

for t in threads:

t.join()

if __name__ == '__main__':

print 'start -->'

main()

print 'go here -->'

运行结果:

start -->

hi , i am a thread. 0

hi , i am a thread. 1

hi , i am a thread. 2

hi , i am a thread. 3

hi , i am a thread. 4

go here -->

2,在类中多使用线程

下面是在类中使用线程的示例:

class MyThread(threading.Thread):

def __init__(self,num):

self.num = num

super(MyThread, self).__init__()

def run(self):

print 'i am a thread,',self.num

time.sleep(1)

def main():

threads = []

for i in range(5):

t = MyThread(i)

threads.append(t)

t.start()

for t in threads:

t.join()

if __name__ == '__main__':

print 'start -->'

main()

print 'go here -->

  • run(),需要重写,编写代码实现所需要的功能。

  • getName(),获得线程对象名称

  • setName(),设置线程对象名称

  • start(),启动线程

  • join([timeout]),等待另一线程结束后再运行。

  • setDaemon(bool),设置子线程是否随主线程一起结束,必须在start() 之前调用,默认为False。

  • isDaemon(),判断线程是否随主线程一起结束。

  • isAlive(),检查线程是否在运行中。

join方法的作用是阻塞主进程(无法执行join以后的语句),主线程等待这个线程结束后,才可以执行下一条指令。多线程多join的情况下,依次执行各线程的join方法,前头一个结束了才能执行后面一个。无参数,则等待到该线程结束,才开始执行下一个线程的join。设置参数后,则等待该线程这么长时间就不管它了(而该线程并没有结束)。不管的意思就是可以执行后面的主进程了。

3,线程同步与互斥锁

线程之所以比进程轻量,其中一个原因就是他们共享内存。也就是各个线程可以平等的访问内存的数据,如果在短时间“同时并行”读取修改内存的数据,很可能造成数据不同步。例如下面的例子:

var = 0

class IncreThread(Thread):

def run(self):

global var

print 'before,var is ',var

var += 1

print 'after,var is ',var

def use_incre_thread():

threads = []

for i in range(50):

t = IncreThread()

threads.append(t)

t.start()

for t in threads:

t.join()

print 'After 10 times,var is ',var

if __name__ == '__main__':

use_incre_thread()

有一个全局变量var,五十个线程,每个线程对var变量进行加 1 运算,但是当你多运行几次后,发现并不是每次的运行结果都是 50,为什么呢?

在var是 10 的时候,线程t1读取了var,这个时刻cpu将控制权给了另一个线程t2。t2线程读到的var也是 10,t1和t2都把var加到 11,当时我们期望的是t1 t2两个线程使var + 2 变成 12。在这里就有了资源竞争,相同的情况也可能发生在其它的线程间,所以出现了最后的结果小于 50 的情况。

为了避免线程不同步造成数据不同步,可以对资源进行加锁。也就是访问资源的线程需要获得锁,才能访问。threading 模块提供了一个 Lock 功能,修改代码如下:

var = 0

lock = Lock()  #创建锁

class IncreThread(Thread):

def run(self):

global var

lock.acquire()  #获取锁

print 'before,var is ',var

var += 1

print 'after,var is ',var

lock.release()  #释放锁

def use_incre_thread():

threads = []

for i in range(50):

t = IncreThread()

threads.append(t)

t.start()

for t in threads:

t.join()

print 'After 10 times,var is ',var

if __name__ == '__main__':

use_incre_thread()

虽然线程可以共享内存,但是一个线程不能影响其他线程内的变量(非全局变量)。

4,死锁

在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。尽管死锁很少发生,但一旦发生就会造成应用的停止响应。下面是一个死锁的例子:

mutex_a = Lock()

mutex_b = Lock()

class MyThread(Thread):

def task_b(self):

if mutex_a.acquire():

print 'thread get a mutex_a',self.name

time.sleep(1)

if mutex_b.acquire():

print 'get a mutex_b',self.name

mutex_b.release()

mutex_a.release()

def task_a(self):

if mutex_b.acquire():

print 'thread get a mutex_b',self.name

time.sleep(1)

if mutex_a.acquire():

print 'get a mutex_a',self.name

mutex_a.release()

mutex_b.release()

def run(self):

self.task_a()

self.task_b()

if __name__ == '__main__':

threads = [MyThread() for i in range(2)]

print threads

for t in threads:

t.start()

线程需要执行两个任务,两个任务都需要获取锁,当两个任务得到锁后,就需要等另外锁释放。

5,可重入锁

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

mutex = threading.RLock()

class MyThread(threading.Thread):

def run(self):

if mutex.acquire(1):

print 'threading gte mutex:',self.name

time.sleep(1)

mutex.acquire()

mutex.release()

mutex.release()

def main():

print 'start main threading:'

threads = [MyThread() for i in range(2)]

for t in threads:

t.start()

for t in threads:

t.join()

print 'end main threading.'

if __name__ == '__main__':

main()

6,后台线程

使用多线程默认情况下,当主线程退出之后,即使子线程没有 join,子线程也依然会继续执行。如果希望主线程退出后,其子线程也退出而不再执行,则需要设置子线程为后台线程。python提供了setDaemon方法,将子线程与主线程进行绑定,当主线程退出时子线程的生命也随之结束。

class MyThread(threading.Thread):

def run(self):

wait_time = random.randrange(1, 10)

print 'thread %s will wait %s s' %(self.name, wait_time)

time.sleep(wait_time)

time.sleep(30)

print 'thread %s finished.' % self.name

def main():

print 'start thread:'

for i in range(3):

t = MyThread()

t.setDaemon(1)

t.start()

print 'end thread.'

if __name__ == '__main__':

main()

运行结果:

start thread:

thread Thread-1 will wait 9 s

thread Thread-2 will wait 1 s

thread Thread-3 will wait 7 s

end thread.

本来子线程需要等待几秒才能结束,但是主线程提前结束了,所以子线程也随主线程结束了。

[python] 线程的更多相关文章

  1. python——线程与多线程进阶

    之前我们已经学会如何在代码块中创建新的线程去执行我们要同步执行的多个任务,但是线程的世界远不止如此.接下来,我们要介绍的是整个threading模块.threading基于Java的线程模型设计.锁( ...

  2. python——线程与多线程基础

    我们之前已经初步了解了进程.线程与协程的概念,现在就来看看python的线程.下面说的都是一个进程里的故事了,暂时忘记进程和协程,先来看一个进程中的线程和多线程.这篇博客将要讲一些单线程与多线程的基础 ...

  3. [python] 线程简介

    参考:http://www.cnblogs.com/aylin/p/5601969.html 我是搬运工,特别感谢张岩林老师! python 线程与进程简介 进程与线程的历史 我们都知道计算机是由硬件 ...

  4. PYTHON线程知识再研习A

    前段时间看完LINUX的线程,同步,信息号之类的知识之后,再在理解PYTHON线程感觉又不一样了. 作一些测试吧. thread:模块提供了基本的线程和锁的支持 threading:提供了更高级别,功 ...

  5. Python 线程(threading) 进程(multiprocessing)

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  6. Python线程:线程的调度-守护线程

    Python线程:线程的调度-守护线程   守护线程与普通线程写法上基本么啥区别,调用线程对象的方法setDaemon(true),则可以将其设置为守护线程.在python中建议使用的是thread. ...

  7. python 线程(一)理论部分

    Python线程 进程有很多优点,它提供了多道编程,可以提高计算机CPU的利用率.既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的. 主要体现在一下几个方面: 进程只能在 ...

  8. python线程同步原语--源码阅读

    前面两篇文章,写了python线程同步原语的基本应用.下面这篇文章主要是通过阅读源码来了解这几个类的内部原理和是怎么协同一起工作来实现python多线程的. 相关文章链接:python同步原语--线程 ...

  9. Python学习——Python线程

    一.线程创建 #方法一:将要执行的方法作为参数传给Thread的构造方法 import threading import time def show(arg): time.sleep(2) print ...

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

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

随机推荐

  1. css3++js钟表

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. JAVA 几种引用类型学习

    1.对象的强.软.弱和虚引用    在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从J ...

  3. Unity5版本的AssetBundle打包方案之打包Scene场景

    using UnityEngine; using System.Collections; using UnityEditor; /// <summary> /// 脚本位置:Editor文 ...

  4. (转)创建Graphics的三种方法

    方法一.利用控件或窗体的Paint事件中的PainEventArgs 在窗体或控件的Paint事件中接收对图形对象的引用,作为PaintEventArgs(PaintEventArgs指定绘制控件所用 ...

  5. log4j打印mybatis sql语句

    Mybatis默认使用有slf4j 必须加上依赖 <dependency> <groupId>org.slf4j</groupId> <artifactId& ...

  6. 六、通过插件如何创建自己的MEL command

    1. MAYA API支持不同类型的plugin (1)Command Plugin——扩充MEL命令 (2)Tool Commands——通过鼠标输出 (3)DG plugin——对场景添加新的操作 ...

  7. ArcGIS Viewer for Flex中引入google map作底图

    在ArcGIS Viewer for Flex开发中,经常需要用到google map作为底图,我们不能通过ArcGIS Viewer for Flex - Application Builder轻易 ...

  8. PDF 生成插件 flying saucer 和 iText

    最近的项目中遇到了需求,用户在页面点击下载,将页面以PDF格式下载完成供用户浏览,所以上网找了下实现方案. 在Java世界,要想生成PDF,方案不少,所以简单做一个小结吧. 在此之前,先来勾画一下我心 ...

  9. Dig out deleted chat messages of App Skype

    Last month Candy was arrested on suspicion of having doing online porn webcam shows, but Candy refus ...

  10. Java基础_内部类、静态内部类、成员内部类、局部内部类、匿名内部类 (转)

    From: http://www.itzhai.com/java-based-notebook-a-static-inner-class-within-a-class-member-within-th ...