Python学习笔记五:线程

线程状态(java中的解释,应该通用与python)

线程在一定条件下,状态会发生变化。线程一共有以下几种状态:

1、新建状态(New):新创建了一个线程对象。

2就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得。

3、运行状态(Running)就绪状态的线程获取了CPU,执行程序代码。

4、阻塞状态(Blocked)阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

阻塞的情况分三种:

(1)等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()notifyAll()方法才能被唤醒,

(2)同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。

(3)其他阻塞:运行的线程执行sleep()join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5、死亡状态(Dead)线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

线程变化的状态转换图如下:

Python3 线程中常用的两个模块为:

  • _thread
  • threading(推荐使用)

thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 "_thread"。

下面将介绍threading模块常用方法: 

1. threading.Lock()

    如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。

    使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间

来看看多个线程同时操作一个变量怎么把内容给改乱了

#!/usr/bin/env python3

import time, threading
balance =0
def run_thread(n):
    global balance
    for i in range(10): 
            balance = balance + n
            print("%s存入%d元,余额%d"% (threading.current_thread().getName(), n , balance))
            balance = balance - n
            print("%s取出%d元,余额%d"% (threading.current_thread().getName() , n, balance))
      
t1 = threading.Thread(target=run_thread, name="小美",args=(5,))
t2 = threading.Thread(target=run_thread, name="小明",args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
小美存入5元,余额5
小美取出5元,余额0
小美存入5元,余额5
小美取出5元,余额0
小美存入5元,余额5
小美取出5元,余额0
小美存入5元,余额5
小美取出5元,余额0
小美存入5元,余额5
小美取出5元,余额0
小美存入5元,余额5
小美取出5元,余额0
小美存入5元,余额5
小美取出5元,余额0
小美存入5元,余额5
小美取出5元,余额0
小美存入5元,余额5
小美取出5元,余额0
小明存入8元,余额8
小美存入5元,余额13
小明取出8元,余额5
小美取出5元,余额0
小明存入8元,余额8
小明取出8元,余额0
小明存入8元,余额8
小明取出8元,余额0
小明存入8元,余额8
小明取出8元,余额0
小明存入8元,余额8
小明取出8元,余额0
小明存入8元,余额8
小明取出8元,余额0
小明存入8元,余额8
小明取出8元,余额0
小明存入8元,余额8
小明取出8元,余额0
小明存入8元,余额8
小明取出8元,余额0
小明存入8元,余额8
小明取出8元,余额0
0

运行结果中,其中一次余额为13元

我们定义了一个共享变量balance,初始值为0,并且启动两个线程,先存后取,理论上结果应该为0,但是,由于线程的调度是由操作系统决定的,当t1、t2交替执行时,只要循环次数足够多,balance的结果就不一定是0了。

  如果我们要确保balance计算正确,就要给对balance操作上一把锁,当某个线程开始执行balance加减操作时,我们说,该线程因为获得了锁,因此其他线程不能同时执行,只能等待,直到锁被释放后,获得该锁以后才能改。由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。创建一个锁就是通过threading.Lock()来实现:

#!/usr/bin/env python3

import time, threading,random


balance =0
def run_thread(n):
    global balance
    for i in range(100):
        # 先要获取锁:
        lock.acquire()
        try:
            # 放心地改吧:
            balance = balance + n
            print("%s存入%d元,余额%d"% (threading.current_thread().getName(), n , balance))
            balance = balance - n
            print("%s取出%d元,余额%d"% (threading.current_thread().getName() , n, balance))
        finally:
            # 改完了一定要释放锁:
            lock.release()
        time.sleep(1)
lock = threading.Lock()
t1 = threading.Thread(target=run_thread, name="小美",args=(5,))
t2 = threading.Thread(target=run_thread, name="小明",args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。

获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。所以我们用try...finally来确保锁一定会被释放。


2. threading.condition()  

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

    acquire()/release():获得/释放 Lock

    wait([timeout]):线程挂起,直到收到一个notify通知或者超时(可选的,浮点数,单位是秒s)才会被唤醒继续运行。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。调用wait()会释放Lock,直至该线程被Notify()、NotifyAll()或者超时线程又重新获得Lock.

    notify(n=1):通知其他线程,那些挂起的线程接到这个通知之后会开始运行,默认是通知一个正等待该condition的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。notify()不会主动释放Lock。

    notifyAll(): 如果wait状态线程比较多,notifyAll的作用就是通知所有线程(这个一般用得少)

#!/usr/bin/python3.4
# -*- coding: utf-8 -*-

import threading, time

def Seeker(cond, name):
    # 添加延时,让Hider优先获得锁
    time.sleep(1)
    cond.acquire()
    print("Seeker获得锁")
    print('%s :我已经把眼睛蒙上了!'% name)

    cond.notify()
    cond.wait()
    for i in range(3):
        print('%s is finding!!!'% name)
        time.sleep(2)
    cond.notify()
    cond.release()
    print('%s :我赢了!'% name)

def Hider(cond, name):
    cond.acquire()
    print("Hider获得锁")
    # time.sleep(100)
    # 一个线程成功获取锁后,其他线程就继续等待直到获得锁为止  wait() 可以释放对象锁,让其他线程拿到之后去执行
    cond.wait()
    for i in range(2):
        print('%s is hiding!!!'% name)
        time.sleep(3)
    print('%s :我已经藏好了,你快来找我吧!'% name)
    cond.notify()
    cond.wait()
    cond.release()
    print('%s :被你找到了,唉~^~!'% name)


if __name__ == '__main__':
    cond = threading.Condition()
    seeker = threading.Thread(target=Seeker, args=(cond, 'seeker'))
    hider = threading.Thread(target=Hider, args=(cond, 'hider'))
    seeker.start()
    hider.start()
"C:\Program Files (x86)\Python36-32\python.exe" D:/python_work/PythonLearning/threadcondition.py
seeker :我已经把眼睛蒙上了!
hider is hiding!!!
hider is hiding!!!
hider :我已经藏好了,你快来找我吧!
seeker is finding!!!
seeker is finding!!!
seeker is finding!!!
hider :被你找到了,唉~^~!
seeker :我赢了!

Process finished with exit code 0



猜你喜欢

转载自blog.csdn.net/yaoliuwei1426/article/details/80784877