32 Python 多线程 - threading

Python 多线程编程 - threading

线程

引入线程:

​ 原因:由于进程拥有自己的资源,故调度付出的开销较大

​ 线程:独立调度和分派的基本单位

​ 通信:互斥锁 条件变量 信号量

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。

另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。

线程也有就绪阻塞运行三种基本状态。

  • 就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;
  • 运行状态是指线程占有处理机正在运行;
  • 阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。

每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。线程是程序中一个单一的顺序控制流程,进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程

Python与线程

Python使用POSIX兼容的线程,即POSIX兼容的线程。但是Python的线程受到的限制很多,因为Python解释器使用了内部的全局解释锁(GIL),Python的执行由Python虚拟机控制,Python解释器可以运行多个线程,但是任意时刻只允许单个线程在解释器中执行,对Python虚拟机的访问由全局解释锁(GIL)控制。GIL保证同一个时刻仅有一个线程在解释器中执行。无论系统上有多少个CPU,Python只能在一个CPU上运行。

(使用GIL的原因,在多线程访问数据时,保证数据安全)

如果线程涉及大量的CPU操作,使用线程会降低程序的运行速度。可结合多进程,加快执行

#!/usr/bin/python
# -*- coding: utf-8 -*-

import time
from threading import Thread
from multiprocessing import Process
from timeit import Timer

def countdown(n):
    while n > 0:
        n -= 1

def t1():
    COUNT=100000000
    thread1 = Thread(target=countdown,args=(COUNT,))
    thread1.start()
    thread1.join()
#    COUNT = 100000000 # 100 million
#    countdown(COUNT)

def t2():
    COUNT=100000000
    thread1 = Thread(target=countdown,args=(COUNT//2,))
    thread2 = Thread(target=countdown,args=(COUNT//2,))
    thread1.start(); thread2.start()
    thread1.join(); thread2.join()

def t3():
    COUNT=100000000
    p1 = Process(target=countdown,args=(COUNT//2,))
    p2 = Process(target=countdown,args=(COUNT//2,))
    p1.start(); p2.start()
    p1.join(); p2.join()

if __name__ == '__main__':
    t = Timer(t1)
    print 'countdown in one thread:',t.timeit(1)
    t = Timer(t2)
    print 'countdown use two thread:',t.timeit(1)
    t = Timer(t3)
    print 'countdown use two Process',t.timeit(1)

    '''
    result:多线程最慢,多进程最快
    countdown in one thread:5.18
    countdown use two thread:18.26
    countdown use two Process:3.22
    '''

Threading

threading模块比thread更为先进,对线程的支持更为完善。

threading模块对象解释
Thread创建一个可执行的线程对象
Lock锁原语对象
RLock可重入锁对象,使单线程可以获得已经获得了的锁(递归锁定)
Condition条件变量对象能让一个线程停下来,等待其他线程满足了某个条件
Event通用的条件变量。多个线程可以等待某个事件的发生,在事件发生后所有的线程都会被激活
Semaphore为等待锁的线程提供一个类似等待室的结构
BoundedSemaphore与Semaphore类似,只是他不允许超过初始值
Timer与Thread类似,只是它等待一段时间后才开始运行
activeCount()当前活动的线程数量
currentThread()返回当前线程对象
enumerate()返回当前活动线程的列表
settrace(func)为所有线程设置一个跟踪函数
setprofile(func)为所有线程设置一个profile函数 threading模块对象

Thread

Thread(group=None, target=None, name=None, args=(), kwargs={})

group,预留参数,一直为None

target,线程启动时执行的可调用对象,由run()方法调用

name,线程名

args,target处可调用对象的参数,如果可调用对象没有参数,不用赋值

kwargs,target处可调用对象的关键字参数

创建一个可执行的线程对象。

Thread的实例有以下属性

  1. start()

    启动线程,这个方法只能调用一次,执行run()方法

  2. run()

    线程启动时将调用此方法。默认情况下,它将调用target,还可以在Thread的子类中重新定义此方法

  3. join(timeout = None)

    timeout,超时时间(单位 = s) ,默认为无时间限制

    等待线程终止或者出现超时为止,能多次使用该方法。

  4. getName(),name

    返回线程名

  5. setName(name)

    设置线程名

  6. isAlive(),is_alive()

    线程正在运行返回True,否则返回False

  7. isDaemon()

    返回线程的daemon状态

  8. setDaemon(daemonic),daemon

    设置线程的daemon状态,一定要在start之前调用

    daemon = True,主线程结束,子线程结束

    daemon = False,主线程结束,子线程不结束,继续执行

code

#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
如果线程
daemon == True,子线程随主线程的结束而结束,
daemon == False,子线程不会随主线程结束...
'''

import threading
import time

class MyThread(threading.Thread):
    def __init__(self,name,lock):
        super(MyThread,self).__init__(name = name)
        self.lock = lock

    def run(self):
        num = 5
        while num:
            num -= 1
            time.sleep(1)
            self.lock.acquire()
            print "This is %s , num = %s"%(self.getName(),num)
            self.lock.release()
            '''与上述加锁方式等价
            with self.lock:
                print "This is %s , num = %s"%(self.getName(),num)
            '''

        self.lock.acquire()
        print "%s  finish "%self.getName()
        self.lock.release()

if __name__ == "__main__":
    '''将两个#打开,运行结果不一样'''
    lock = threading.Lock()
    t1 = MyThread('son1',lock)
    t2 = MyThread('son2',lock)
    #t1.daemon = True
    #t2.daemon = True
    print t1.daemon,t2.daemon
    print t1.is_alive()
    t1.start();t2.start()
    print "father thread finish"

Timer

Timer(interval, function, args=[], kwargs={})

interval,时间,单位 = s

创建定时器对象(Thread的子类),在interval秒之后执行func。在调用start()方法后启动定时器。

Timer实例具有以下属性(Thread的子类,也是线程)

  1. start()

    启动定时器

  2. cancel()

    如果定时器尚未执行,取消定时器

#!/usr/bin/python
# -*- coding: utf-8 -*-

import threading
import time

def fun():
    print 'Timer',time.ctime()        

if __name__ == "__main__":
    t = threading.Timer(3,fun)
    t.start()
    print 'main',time.ctime()

Lock

lock = threading.Lock()

'''使用锁'''
lock.acquire()
dosomething()
lock.release()

'''锁支持上下文管理协议,离开上下文时自动释放锁,与上述等价'''
with lock:
    dosomething()

Condition

Condition([lock])

lock,RLock/Lock实例,默认为RLock实例

创建条件变量

  1. notify(n = 1)

    唤醒n个等待该条件变量的线程,如果调用的线程未对该条件变量上锁(即使用该条件变量的时候一定要对该条件变量上锁),会抛出RuntimeError异常

    By default, wake up one thread waiting on this condition, if any. If the calling thread has not acquired the lock when this method is called, a RuntimeError is raised.

    This method wakes up at most n of the threads waiting for the condition variable; it is a no-op if no threads are waiting.

    The current implementation wakes up exactly n threads, if at least n threads are waiting. However, it’s not safe to rely on this behavior. A future, optimized implementation may occasionally wake up more than n threads.

    Note: an awakened thread does not actually return from its wait() call until it can reacquire the lock. Since notify() does not release the lock, its caller should.

  2. notifyAll()

    唤醒所有线程,如果调用的线程未对该条件变量上锁(即使用该条件变量的时候一定要对该条件变量上锁),会抛出RuntimeError。

    Wake up all threads waiting on this condition. This method acts like notify(), but wakes up all waiting threads instead of one. If the calling thread has not acquired the lock when this method is called, a RuntimeError is raised.

  3. wait(timeout=0)

    Wait until notified or until a timeout occurs. If the calling thread has not acquired the lock when this method is called, a RuntimeError is raised.

    This method releases the underlying lock, and then blocks until it is awakened by a notify() or notifyAll() call for the same condition variable in another thread, or until the optional timeout occurs. Once awakened or timed out, it re-acquires the lock and returns.

    When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof).

    When the underlying lock is an RLock, it is not released using its release() method, since this may not actually unlock the lock when it was acquired multiple times recursively. Instead, an internal interface of the RLock class is used, which really unlocks it even when it has been recursively acquired several times. Another internal interface is then used to restore the recursion level when the lock is reacquired.

  4. acquire()

    上锁

  5. release()

    释放锁

#!/usr/bin/python
# -*- coding: utf-8 -*-

import threading, time

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()
        print self.name + u': 我已经把眼睛蒙上了'

        """
        notify源码解析:
            __waiters = self.__waiters
            waiters = __waiters[:n] # 获取等待队列中的n个等待锁
            for waiter in waiters:
            waiter.release() # 释放Hider的等待锁
            try:
                __waiters.remove(waiter)
            except ValueError:
                pass
        """
        # 释放n个waiter锁,waiter线程准备执行
        self.cond.notify()

        print('notifyed...')

        # 释放condition条件锁,waiter线程Hider真正开始执行
        self.cond.wait()
        print('waited...')

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

        print self.name + u': 我赢了'

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

        """
        wait()源码解析:
            waiter = _allocate_lock() # 创建一把等待锁,加入waiters队列,等待notify唤醒
            waiter.acquire() # 获取锁
            self.__waiters.append(waiter)
            saved_state = self._release_save() # 释放condition.lock全局条件锁,以便其他等待线程执行
            if timeout is None:
                waiter.acquire() # 再次获取锁,因为已经锁定无法继续,等待notify执行release
        """
        # wait()释放对琐的占用,同时线程挂起在这里,直到被notify并重新占有琐。
        self.cond.wait()

        print self.name + u': 我已经藏好了,你快来找我吧'
        self.cond.notify()
        self.cond.wait()
        self.cond.release()
        print self.name + u': 被你找到了,哎~~~'

cond = threading.Condition()

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

hider.join()
seeker.join()
print('end...')

转载请标明出处,原文地址(http://blog.csdn.net/lis_12/article/details/54564813).

如果觉得本文对您有帮助,请点击‘顶’支持一下,您的支持是我写作最大的动力,谢谢。

参考网址

  1. http://www.361way.com/thread-condition/4621.html
  2. https://docs.python.org/2/library/threading.html#thread-objects
  3. http://blog.csdn.net/lis_12/article/details/54564703
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值