Python threading 多线程_Thinking_boy1992的博客-程序员秘密

技术标签: python  线程  多线程  Python  

threading通过对thread模块进行二次封装,提供了更方便的API来操作线程。


threading.Thread

Thread 是threading模块中最重要的类之一,可以使用它来创建线程。有两种方式来创建线程:一种是通过继承Thread类,重写它的run方法;另一种是创建一个threading.Thread对象,在它的初始化函数(init)中将可调用对象作为参数传入。下面分别举例说明。先来看看通过继承threading.Thread类来创建线程的例子:

#coding=gbk
import threading, time, random
count = 0
class Counter(threading.Thread):
    def __init__(self, lock, threadName):
        '''@summary: 初始化对象。

        @param lock: 琐对象。
        @param threadName: 线程名称。
        '''
        super(Counter, self).__init__(name = threadName)  #注意:一定要显式的调用父类的初始
化函数。
        self.lock = lock

    def run(self):
        '''@summary: 重写父类run方法,在线程启动后执行该方法内的代码。
        '''
        global count
        self.lock.acquire()
        for i in xrange(10000):
            count = count + 1
        self.lock.release()
lock = threading.Lock()
for i in range(5): 
    Counter(lock, "thread-" + str(i)).start()
time.sleep(2)   #确保线程都执行完毕
print count

在代码中,我们创建了一个Counter类,它继承了threading.Thread。初始化函数接收两个参数,一个是琐对象,另一个是线程的名称。在Counter中,重写了从父类继承的run方法,run方法将一个全局变量逐一的增加10000。在接下来的代码中,创建了五个Counter对象,分别调用其start方法。最后打印结果。这里要说明一下run方法 和start方法: 它们都是从Thread继承而来的,run()方法将在线程开启后执行,可以把相关的逻辑写到run方法中(通常把run方法称为活动[Activity]。);start()方法用于启动线程。
再看看另外一种创建线程的方法:

count = 0
lock = threading.Lock()
def doAdd():
    '''@summary: 将全局变量count 逐一的增加10000。
    '''
    global count, lock
    lock.acquire()
    for i in xrange(10000):
        count = count + 1
    lock.release()
for i in range(5):
    threading.Thread(target = doAdd, args = (), name = 'thread-' + str(i)).start()
time.sleep(2)   #确保线程都执行完毕
print count

在这段代码中,我们定义了方法doAdd,它将全局变量count 逐一的增加10000。然后创建了5个Thread对象,把函数对象doAdd 作为参数传给它的初始化函数,再调用Thread对象的start方法,线程启动后将执行doAdd函数。这里有必要介绍一下threading.Thread类的初始化函数原型:

def __init__(self, group=None, target=None, name=None, args=(), kwargs={})
# 参数group是预留的,用于将来扩展;
# 参数target是一个可调用对象(也称为活动[activity]),在线程启动后执行;
# 参数name是线程的名字。默认值为“Thread-N“,N是一个数字。
# 参数args和kwargs分别表示调用target时的参数列表和关键字参数。

Thread类还定义了以下常用方法与属性:

Thread.getName()
Thread.setName()
Thread.name
#用于获取和设置线程的名称。
Thread.ident
# 获取线程的标识符。线程标识符是一个非零整数,只有在调用了start()方法之后该属性才有效,否则它只返回None
Thread.is_alive()
Thread.isAlive()
# 判断线程是否是激活的(alive)。从调用start()方法启动线程,到run()方法执行完毕或遇到未处理异常而中断 这段时间内,线程是激活的。

Thread.join([timeout])

调用Thread.join将会使主调线程堵塞,直到被调用线程运行结束或超时。参数timeout是一个数值类型,表示超时时间,如果未提供该参数,那么主调线程将一直堵塞到被调线程结束。下面举个例子说明join()的使用:

import threading, time
def doWaiting():
    print 'start waiting:', time.strftime('%H:%M:%S')
    time.sleep(3)
    print 'stop waiting', time.strftime('%H:%M:%S')
thread1 = threading.Thread(target = doWaiting)
thread1.start()
time.sleep(1)  #确保线程thread1已经启动
print 'start join'
thread1.join()  #将一直堵塞,直到thread1运行结束。
print 'end join'
threading.RLock

threading.Lock

在threading模块中,定义两种类型的琐:threading.Lock和threading.RLock。它们之间有一点细微的区别,通过比较下面两段代码来说明:

import threading
lock = threading.Lock() #Lock对象
lock.acquire()
lock.acquire()  #产生了死琐。
lock.release()
lock.release()
import threading
rLock = threading.RLock()  #RLock对象
rLock.acquire()
rLock.acquire() #在同一线程内,程序不会堵塞。
rLock.release()
rLock.release()

这两种琐的主要区别是:RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。

threading.Condition

可以把Condiftion理解为一把高级的琐,它提供了比Lock, RLock更高级的功能,允许我们能够控制复杂的线程同步问题。threadiong.Condition在内部维护一个琐对象(默认是RLock),可以在创建Condigtion对象的时候把琐对象作为参数传入。Condition也提供了acquire, release方法,其含义与琐的acquire, release方法一致,其实它只是简单的调用内部琐对象的对应的方法而已。Condition还提供了如下方法(特别要注意:这些方法只有在占用琐(acquire)之后才能调用,否则将会报RuntimeError异常。):

Condition.wait([timeout]):

wait方法释放内部所占用的琐,同时线程被挂起,直至接收到通知被唤醒或超时(如果提供了timeout参数的话)。当线程被唤醒并重新占有琐的时候,程序才会继续执行下去。

Condition.notify():

唤醒一个挂起的线程(如果存在挂起的线程)。注意:notify()方法不会释放所占用的琐。

Condition.notify_all()

Condition.notifyAll()

唤醒所有挂起的线程(如果存在挂起的线程)。注意:这些方法不会释放所占用的琐。

现在写个捉迷藏的游戏来具体介绍threading.Condition的基本使用。假设这个游戏由两个人来玩,一个藏(Hider),一个找(Seeker)。游戏的规则如下:1. 游戏开始之后,Seeker先把自己眼睛蒙上,蒙上眼睛后,就通知Hider;2. Hider接收通知后开始找地方将自己藏起来,藏好之后,再通知Seeker可以找了; 3. Seeker接收到通知之后,就开始找Hider。Hider和Seeker都是独立的个体,在程序中用两个独立的线程来表示,在游戏过程中,两者之间的行为有一定的时序关系,我们通过Condition来控制这种时序关系。

#---- Condition
#---- 捉迷藏的游戏
import threading, time
class Hider(threading.Thread):
    def __init__(self, cond, name):
        super(Hider, self).__init__()
        self.cond = cond
        self.name = name

    def run(self):
        time.sleep(1) #确保先运行Seeker中的方法   

        self.cond.acquire() #b    
        print self.name + ': 我已经把眼睛蒙上了'
        self.cond.notify()
        self.cond.wait() #c    
                         #f 
        print self.name + ': 我找到你了 ~_~'
        self.cond.notify()
        self.cond.release()
                            #g
        print self.name + ': 我赢了'   #h

class Seeker(threading.Thread):
    def __init__(self, cond, name):
        super(Seeker, self).__init__()
        self.cond = cond
        self.name = name
    def run(self):
        self.cond.acquire()
        self.cond.wait()    #a    #释放对琐的占用,同时线程挂起在这里,直到被notify并重新占
有琐。
                            #d
        print self.name + ': 我已经藏好了,你快来找我吧'
        self.cond.notify()
        self.cond.wait()    #e
                            #h
        self.cond.release() 
        print self.name + ': 被你找到了,哎~~~'

cond = threading.Condition()
seeker = Seeker(cond, 'seeker')
hider = Hider(cond, 'hider')
seeker.start()
hider.start()
threading.Event

Event实现与Condition类似的功能,不过比Condition简单一点。它通过维护内部的标识符来实现线程间的同步问题。(threading.Event和.NET中的System.Threading.ManualResetEvent类实现同样的功能。)

Event.wait([timeout])

堵塞线程,直到Event对象内部标识位被设为True或超时(如果提供了参数timeout)。

Event.set()

将标识位设为Ture

Event.clear()

将标识伴设为False。

Event.isSet()

判断标识位是否为Ture。
下面使用Event来实现捉迷藏的游戏(可能用Event来实现不是很形象)

#---- Event
#---- 捉迷藏的游戏
import threading, time
class Hider(threading.Thread):
    def __init__(self, cond, name):
        super(Hider, self).__init__()
        self.cond = cond
        self.name = name

    def run(self):
        time.sleep(1) #确保先运行Seeker中的方法   

        print self.name + ': 我已经把眼睛蒙上了'

        self.cond.set()

        time.sleep(1)   

        self.cond.wait()
        print self.name + ': 我找到你了 ~_~'

        self.cond.set()

        print self.name + ': 我赢了'

class Seeker(threading.Thread):
    def __init__(self, cond, name):
        super(Seeker, self).__init__()
        self.cond = cond
        self.name = name
    def run(self):
        self.cond.wait()

        print self.name + ': 我已经藏好了,你快来找我吧'
        self.cond.set()

        time.sleep(1)
        self.cond.wait()

        print self.name + ': 被你找到了,哎~~~'

cond = threading.Event()
seeker = Seeker(cond, 'seeker')
hider = Hider(cond, 'hider')
seeker.start()
hider.start()
threading.Timer

threading.Timer是threading.Thread的子类,可以在指定时间间隔后执行某个操作。下面是Python手册上提供的一个例子:

def hello():
    print "hello, world"
t = Timer(3, hello)
t.start() # 3秒钟之后执行hello函数。

threading模块中还有一些常用的方法没有介绍:

threading.active_count()
threading.activeCount()
#获取当前活动的(alive)线程的个数
threading.current_thread()
threading.currentThread()
#获取当前的线程对象(Thread object)。
threading.enumerate()
#获取当前所有活动线程的列表。
threading.settrace(func)
#设置一个跟踪函数,用于在run()执行之前被调用。
threading.setprofile(func)
#设置一个跟踪函数,用于在run()执行完毕之后调用。
Thread.setDaemon()

setDaemon需要在start方法调用之前使用, 线程划分为用户线程和后台(daemon)进程,setDaemon将线程设置为后台进程

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Thinking_boy1992/article/details/70764366

智能推荐

gcc # 和 ##_June_Hou的博客-程序员秘密

#和##的用法1.'#' 用于转换字符串,可以是任意字符2."##" 用于连接两边的符号,例如:a##b == ab, ab必须为一个为已声明变量; a##b() == ab(), ab()必须为一个已声明的函数.举例:#include <stdio.h>#define AAA(a)      #a      #define BBB(b,c)    b##c#defi...

C语言 递归实现顺序输出整数_Shuo..的博客-程序员秘密

题目描述本题要求实现一个函数,对一个整数进行按位顺序输出。函数接口定义:void printdigits( int n );函数printdigits应将n的每一位数字从高位到低位顺序打印出来,每位数字占一行。裁判测试程序样例:#include <stdio.h>void printdigits( int n );int main(){int n; scanf("...

Dubbo入门-分布式原理详解--搭建一个最简单的Demo框架_xfl4629712的博客-程序员秘密

Dubbo背景和简介Dubbo开始于电商系统,因此在这里先从电商系统的演变讲起。1,单一应用框架(ORM) 当网站流量很小时,只需一个应用,将所有功能如下单支付等都部署在一起,以减少部署节点和成本。 缺点:单一的系统架构,使得在开发过程中,占用的资源越来越多,而且随着流量的增加越来越难以维护 2.垂直应用框架(MVC) 垂直应用架构解决了单一应用架构所面临的扩容问题,流

mysql -vvv_MySQL_weixin_39539764的博客-程序员秘密

MySQL编码一、关闭、启动二、MySQL版本$ mysql -V$ mysql--help | grepDistribmysql>status;mysql> select version();三、配置文件位置mysqld --help --verbose|grep -A1 -B1 cnf四、用户及权限管理创建用户CREATE USER 'username'@'host' IDENTI...

Samsung ARTIK 530 cloud connectivity test_scruffybear的博客-程序员秘密

SummaryCreating a New Device in ARTIK cloudGetting the Device TokenCreating RulesUsing Switch control the LED through cloudtest result 1 Control LEDs remotely via ARTIK Cloudtest result 2 Control

1.创建springBoot项目_weixin_33946020的博客-程序员秘密

为什么80%的码农都做不了架构师?>>> ...

随便推点

安装Teradata 数据库驱动_dqsweet的博客-程序员秘密_teradata驱动下载

Step to install the Teradata Client ODBC1. For installing the Teradata client you need to follow the following sequence for installing.2. First install Teradata TDGSS2. Then install Teredata T

canal小试牛刀第一篇:canal-server+kafka根据mysql binlog同步数据_凯丨的博客-程序员秘密_canal-server

1. 前情提要直接监听mysql的binlog同步数据可以对业务无侵入。数仓搭建必备利器。2. 准备工作了解canal-server了解kafka了解Canal Kafka了解mysql主从了解docker、docker-compose3. 配置相关mysql保存以下配置至./mysql/conf.d/slave.cnf[mysqld]log-bin=mysql-bin # 开启 binlogbinlog-format=ROW # 选择 ROW 模式server_id=1

HDOJ-1102 Constructing Roads(最小生成树)两种做法_huatian5的博客-程序员秘密

题目最小生成树Kruskal算法/*HDU 1102 Constructing Roads题意:有n个乡村,修路,然后保证每两条路之间连接。如果A和B连接,那么A和B直接连接或者中间还有一个乡村,间接连接。已经有一些路了,你的任务是找出来一路径连接所有乡村。输入:i到j的长度,注意去重边最小生成树,,,*/#include#include#includeusing

Unable to create tempDir. java.io.tmpdir is set to C:\Users\ADMINI~1\AppData_大强012的博客-程序员秘密

因为未知原因,jdk-8u212-windows-x64.exe安装不上,拷贝别人安装好的文件夹C:\Program Files\Java\jdk1.8.0_231到自己的电脑,后端程序可以执行,但报以下错误信息:Unable to create tempDir. java.io.tmpdir is set to C:\Users\ADMINI~1\AppData解决办法:删掉拷贝别人...

利用datax数据同步工具将MySLQ的数据导入到Hbase数据库-----------全套流程_小帅热爱难回头的博客-程序员秘密_datax 同步到hbase

第一:MySQL数据库中的表的结构如下:DROP TABLE IF EXISTS mhl_device;CREATE TABLE mhl_device (0 id bigint(20) NOT NULL,1 update_time datetime DEFAULT NULL COMMENT ‘更新时间’,2 create_time datetime DEFAULT NULL COMMEN...

如何精确的设置行与行之间的间距_做一个不秃头的程序员的博客-程序员秘密_vue设置行间距

这是一篇专门针对 刚入门 前端的小白们,作为个前端开发人员,我们应该要具备的就是 99.9%的还原设计图,那么必不可少的就是设置页面中文字的上下间距问题,也是刚入门的人员比较难的一个点,怎样才可以写的跟设计图的一模一样呢。比如这样的一个图片,里面有几种字体大小和行间距都不一样,算是一个比较复杂一点的:以下我介绍两种设置这个间距的方法:1.只用line-height(行高)使用行...