Python-多线程之threading


------------恢复内容开始------------

线程基础

1.1线程状态

线程有五种状态:新建、就绪、运行、阻塞、死亡

 

1.2线程同步

  因为线程同时运行多个任务,但实际上还是cpu以极快的速度在每个线程之间转换处理任务,对于python这种高级语言,每条语句的执行都不是简单的一步到位,因此,在语句执行过程中,还未结束(比如打印两句话),cpu就运行其他的线程,这可能会导致公共资源发生不好的变化(第一句话还没完,中间就插入了第二句话),也就是数据不同步了,因此引用了锁的概念,避免重要数据在处理时被打断或者其他线程篡改。

  锁有两种状态——锁定、未锁定,当锁定的时候,其他的线程执行到锁定的部分,需要请求锁,但是锁一次只供给一条线程,等这条先线程执行到释放锁的时候,其他的等待(被阻塞)的线程中,才有一条线程可以获得锁(线程进行竞争来获取锁,不知道那线程得到锁),然后继续执行、释放等,这样的话重要数据的操做会完整执行,保证了数据的同步。

线程和锁的关系

 

1.3线程通信(条件变量)

  线程同步使用了锁,保证了只让一条线程执行,其他等待,我们认为其他的线程被阻塞了,且这些线程的等待是被动的,释放锁后会立刻竞争锁。虽然能保证线程不干扰了,但是有一个问题,我们怎么能保证最开始执行的线程时我们向要执行的线程呢,毕竟,线程执行的任务不一定全部一样,有的线程需要其他线程创造一些条件,才能正常无错误的执行,比如a线程创建了一个列表,b线程才可以遍历列表,如果b线程先遍历,那么就会报错。因此引入了条件变量的概念

  条件变量,允许在不满足条件的时候,线程进入等待,当其他线程创造条件后,发出通知,收到通知的线程直到可以干活了,就从挂起状态运行,这时候,如不收不到通过通知,就会一直挂着。

  当一个线程得到锁定后,判断是否满足条件。如果不满足,则回到等待池(此时自动解锁),等待条件满足的通知,然后收到通知后,请求锁定,直到获得锁定,条件满足,继续执行,然后释放锁给其它线程

  挂起相对于阻塞来说,可以看为是一个主动的行为,因为如果一致不满足,及时锁释放了,被挂起的线程也不会取竞争锁

当满足某种条件后,认为需要唤醒等待线程来执行任务,就可以使用 nodify() 后者nodify_all()唤起一条或所等待池中的线程

 

1.4、线程阻塞之间的转换

阻塞有三种情况:
同步阻塞是指处于竞争锁定的状态,线程请求锁定时将进入这个状态,一旦成功获得锁定又恢复到运行状态;
等待阻塞是指等待其他线程通知的状态,线程获得条件锁定后,调用“等待”将进入这个状态,一旦其他线程发出通知,线程将进入同步阻塞状态,再次竞争条件锁定;
而其他阻塞是指调用time.sleep()、anotherthread.join()或等待IO时的阻塞,这个状态下线程不会释放已获得的锁定。

tips: 如果能理解这些内容,接下来的主题将是非常轻松的;并且,这些内容在大部分流行的编程语言里都是一样的。(意思就是非看懂不可 >_< 嫌作者水平低找别人的教程也要看懂)

 

 

python多线程——threading使用

概述:

  threading用于提供跟线程相关的操作,线程是应用程序中工作的最小单元。

threading模块提供的类

  Thread、Lock、Rlock、Condition、Semaphore、Event、Timer、local

threading模块提供的 常用方法

  threading.currentThreading()  返回现在进行的线程变量

  threading.enumerate()  返回当前正在运行的所有进程的列表

  threading.activeCount()  返回活跃的线程的数量,与len(threading.enumrate())相同

 

Thread类

  Thread类是线程类,使用方式为传入要运行的方法和继承Thread

import threading
import time

# method1 将要执行的方法,作为一个参数,在实例化Threading类的时候,作为目标直接传入
def loop(x,y):
    print('%s is running' % threading.currentThread())
    time.sleep(3)
    print('%s is end' % threading.currentThread())
    return x+y

for i in range(4):
    t = threading.Thread(target=loop,name='线程'+str(i),args=(i,5))
    t.start()
    time.sleep(1)
    print()
print('\nmian thread end')


# method2 从Thread类继承,继承的对象重写的调用run方法
class LoopClass(threading.Thread):
    def __init__(self,name):
        super(LoopClass,self).__init__()  # 显式调用父类初始化函数(暂时不理解为什么)
        self.name = name

    def run(self) -> None:
        print(self.name,'is runnig' )
        print('当前进程有======>',threading.enumerate())

for i in range(4,7):
    l=LoopClass(i)
    l.run()

#
# <Thread(线程0, started 10984)> is running
#
# <Thread(线程1, started 12096)> is running
#
# <Thread(线程2, started 956)> is running
# <Thread(线程0, started 10984)> is end
#
# <Thread(线程3, started 4256)> is running
# <Thread(线程1, started 12096)> is end
#
#
# mian thread end
# 4 is runnig
# 当前进程有======> [<_MainThread(MainThread, started 12068)>, <Thread(线程2, started 956)>, <Thread(线程3, started 4256)>]
# 5 is runnig
# 当前进程有======> [<_MainThread(MainThread, started 12068)>, <Thread(线程2, started 956)>, <Thread(线程3, started 4256)>]
# 6 is runnig
# 当前进程有======> [<_MainThread(MainThread, started 12068)>, <Thread(线程2, started 956)>, <Thread(线程3, started 4256)>]
# <Thread(线程2, started 956)> is end
# <Thread(线程3, started 4256)> is end

 

Thread类构造方法

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

  group:线程组,当前未实现,默认为None

  target:可执行的方法

  name:线程名

实例方法:

  isAlive()  线程是否活跃

  setName()  设置线程名

  getName()  获取线程名

  start()  启动线程

  is/setDeamon(bool)  获取/设置为后台线程(默认前台线程为False),在start之前设置

  后台线程(守护线程):相当于线程的一种属性,当主线程执行完的时候,如果后台线程没有执行结束,也会结束程序

  前台线程:当主线程执行结束的时候,如果有前台线程还在执行,则等待前台线程执行结束,然后程序结束

def loop(n):
    while n:
        print('%s is running' % threading.currentThread())
        time.sleep(2)
    return 1

for i in range(4):
    t = threading.Thread(target=loop,name='线程'+str(i),args=(1,))
    t.setDaemon(True) # 设置为守护进程,
    t.start()
for i in range(4):
    td = threading.Thread(target=loop,name='线程'+str(i),args=(2,))
    td.setDaemon(True)
    td.start()

print('\nmian thread end')
# 当两个进程都无限循环,但是都被设置为守护进程,主线程不会无限等待,正常终止
# <Thread(线程0, started daemon 4132)> is running
# <Thread(线程1, started daemon 11216)> is running
# <Thread(线程2, started daemon 11640)> is running
# <Thread(线程3, started daemon 5844)> is running
# mian thread end
# 当某一个进程设置为前台进程,即便主线程执行完,程序也不会结束

  join([timeout]) 阻塞上下文环境的线程,直到调用此方法的线程终止,或者达到timeout,(及时线程设置守护线程,也需要等待)

import threading
import time

def loop1():
    # time.sleep(1)
    print('This is %s is running' % threading.currentThread().getName())
    time.sleep(1)
    print('%s ending' % threading.currentThread().getName())

thread_list = []
for i in range(4):
    a = threading.Thread(target=loop1)
    thread_list.append(a)
    a.start()

for thread in thread_list:
    thread.join()

print('this is main ')

# This is Thread-1 is running
# This is Thread-2 is running
# This is Thread-3 is running
# This is Thread-4 is running
# Thread-1 endingThread-2 endingThread-3 ending
# 
# 
# Thread-4 ending
# this is main 
# 
# 进程已结束,退出代码为 0

 

Lock和RLock类

  线程之间随机调度,某条线程执行的语句还未结束,cpu就转换到其他的线程上了,为了避免操作同一个线程的时候,资源不产生混乱,使用锁,保证公共的变量不被多条线程同时执行。

  Lock(指令锁),是最低级的同步指令,当Lock处在锁定状态的时候,不被特定的线程所有,.Lock包含了锁定、非锁定两种状态,以及aquest和release两种方法。请求锁定时,线程将处于锁定状态,直到释放

  RLock(可重入锁)可以被一个线程对此请求的同步指令。RLock有”拥有的线程“和”递归等级“的概念,RLock被某个线程所有,该线程可以再次调用acquire(),release()时需要相同的次数

  可以认为RLock包含一个计数器,当成功调用acquire/release时,计数器+1/-1,当计数器为0时,认为是未锁定状态

  相对于Lock,RLock可以重复aquire而不会出现死锁的情况

  lock.require([,timeout])  

  lock.release()

import threading

lock = threading.Lock()  # Lock对象
lock.acquire()
print(1)
lock.acquire()  # 产生了死锁。
print(2)
lock.release()
print(3)
lock.release()
print(lock.acquire())
# 无法结束
# 1  

import threading

lock = threading.RLock() # RLock对象
lock.acquire()
print(1)
lock.acquire()
print(2)
lock.release()
print(3)
lock.release()
print(lock.acquire())

# 正常结束
# 1
# 2
# 3
# True

 Condition

  condition(条件变量),是一个与锁关联的

  可以认为,condition是更强大的带锁功能的类,与condition相关的概念有线程的阻塞和挂起

  阻塞:因为资源的争抢,线程被动的停止。当一个线程获得了锁,其他的线程都需要等待这个线程释放锁之后才能进行,那么这些等待的线程就是被锁定了

  挂起:主动的行为,可以主动挂起某个线程,当这个线程暂时不运行,也能主动放下线程让其继续运行

  挂起的层级是高于阻塞的:当一个线程阻塞的时候,是可以被挂起的,挂起结束后,如果线程还在阻塞就不会运行。当一个线程挂起的时候,如果这个线程,在挂起期间阻塞消除了,如果不放下的话,线程仍旧无法运行

  构造方法Condition([,Lock/RLock])

  实例方法:

    acquire([,timeout])/release:  请求锁、释放锁

    wait([,timeout]):获取锁定之后,调用此方法,锁定的线程将自动解锁,并处于挂起状态,收到广播之后被唤醒,或者timeout超时,才能被唤醒,唤醒后的线程会重新获得锁)

    notify():通知,调用该方法之后,会随机唤醒一个被挂起的线程,只是唤起,而不涉及释放锁,使用前先成功要已经获得锁

    notify_all:唤起所有挂起的线程,使用前线程要已经获得锁

    

Event

  线程通信机制,一个线程通知事件,其他线程等待事件,Event内置一个False的标志,当调用set()的时候,设置为True,调用Clear的是时候,设置为False,wait([,timeout]) 将根据Event是False还是True,将线程阻塞

实例方法  

isSet() : 当内置事件为True时返回True

set():将event内置事件设置为True

clear():将event内置事件设置为False

wait():如果event为False,将阻塞线程,否则立刻返回

 

 

 

 

------------恢复内容结束------------


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM