天天看点

python基础 — 多线程模块 threading

学习python多线程之前先思考两个问题:

1、为什么要使用多线程?

2、python多线程运行机制?

GIL-全局解释器锁

官网地址:https://docs.python.org/3.8/library/threading.html?highlight=threading#barrier-objects

文章目录:

1、线程类 threading

2、线程对象 threading.Thead()

2.1、创建线程的两种方法

2.2、守护线程

3、线程锁对象

3.1、互斥锁    threading.Lock   

3.2、递归锁    threading.RLock

4、条件对象    threading.Condition()

5、信号量对象    threading.Semaphore()

6、事件对象 threading.Event

7、Timer计时器对象    threading.Timer

1、线程类 threading

  • current_thread()    返回与Thread调用方的控制线程相对应的当前对象。
  • enumerate()            返回Thread当前所有活动对象的列表。
  • active_count()    返回Thread当前活动的对象数。返回的计数等于所返回列表的长度enumerate()。
  • main_thread()     返回主要Thread对象。在正常情况下,主线程是启动Python解释器的线程。
  • get_ident()      返回当前线程的“线程标识符”。这是一个非零整数。它的值没有直接的意义。

2、线程对象 threading.Thead()

  • start()    启动线程的活动。
  • join()    等待线程终止。这将阻塞调用线程,直到join()被调用方法的线程终止(正常或通过未处理的异常终止),或者直到发生可选的超时。
  • setDaemon()    守护线程
  • getName()获取当前活动线程的名称

2.1、创建线程的两种方法

"""
创建线程的方法一:
创建一个threading.Thread()对象,参数如下:
_init__(self, group=None, target=None, name=None,args=(), kwargs=None, *, daemon=None)
group:group参数必须为空,参数group是预留的,用于将来扩展;
target: 参数target是一个可调用对象(也称为活动[activity]),在线程启动后执行
name: 参数name是线程的名字。默认值为“Thread-N“,N是一个数字。
args:以元组的方式,为 target 指定的方法传递参数;
kwargs:以字典的方式,为 target 指定的方法传递参数;
daemon:指定所创建的线程是否为后代线程。

"""
import threading
import time


def run():
    """
    threading.current_thread().getName() 获取当前线程的名字,默认Thread-n格式
    如果创建线程组的时候参数name有值,则取指定的name值。
    :return:
    """
    print(threading.current_thread().getName(),time.ctime())
    time.sleep(1)
    print(threading.current_thread().getName(),time.ctime())
    time.sleep(1)
    print(threading.current_thread().getName(),time.ctime())
    time.sleep(1)
    print(threading.current_thread().getName(),time.ctime())
    time.sleep(1)

if __name__ == '__main__':
    """不带参数的线程 """
    t1 = threading.Thread(target=run)
    t2 = threading.Thread(target=run)
    t1.start()
    t2.start()


    #也可以使用for方法来实现多个线程,也可以将多个线程放在一个列表中来遍历
    # for i in range(5):
    #     t=threading.Thread(target=function)
    #     t.start()



D:\github\Python\threading_study>python threading_study01.py
Thread-1 Thu Oct 15 11:10:55 2020
Thread-2 Thu Oct 15 11:10:55 2020
Thread-1 Thu Oct 15 11:10:56 2020
Thread-2 Thu Oct 15 11:10:56 2020
Thread-1 Thu Oct 15 11:10:57 2020
Thread-2 Thu Oct 15 11:10:57 2020
Thread-1 Thu Oct 15 11:10:58 2020
Thread-2 Thu Oct 15 11:10:58 2020

D:\github\Python\threading_study>
           
"""
创建线程的方法二:
自定义线程:继承threading.Thread来定义线程类,然后重写__init__方法和run方法,使用start方法来启动线程。

"""
import threading
import time

class MyThread(threading.Thread):

    def __init__(self):
        """
        重写__init__方法,可以使用super来调用父类的方法
        :param name:
        """
        # super().__init__()
        threading.Thread.__init__(self)

    def run(self):
        """
        重写run方法,注意方法名只能是 run
        这里也可以调用其他方法
        :return:
        """
        print(threading.current_thread().getName(), time.ctime())
        time.sleep(1)
        print(threading.current_thread().getName(), time.ctime())
        time.sleep(1)
        print(threading.current_thread().getName(), time.ctime())
        time.sleep(1)
        print(threading.current_thread().getName(), time.ctime())

if __name__ == '__main__':
    t1 = MyThread()
    t2 = MyThread()
    t1.start()
    t2.start()



D:\github\Python\threading_study>python threading_study02.py
Thread-1 Thu Oct 15 11:12:43 2020
Thread-2 Thu Oct 15 11:12:43 2020
Thread-1 Thu Oct 15 11:12:44 2020
Thread-2 Thu Oct 15 11:12:44 2020
Thread-1 Thu Oct 15 11:12:45 2020
Thread-2 Thu Oct 15 11:12:45 2020
Thread-1 Thu Oct 15 11:12:46 2020
Thread-2 Thu Oct 15 11:12:46 2020

D:\github\Python\threading_study>
           

2.2、守护线程

"""
守护线程:
主线程不管守护线程的执行情况,只要是其他子线程结束且主线程执行完毕,主线程都会关闭。

常见方法:
1、setDaemon(True)方法可以把子线程设置为主线程的守护线程,此方法必须在start之前
2、join()方法,让主线程等待子线程执行,此方法在start之后

"""

import threading
import time

def run1(name,n):
    for _ in range(n): #_下划线表示临时变量, 仅用一次,后面无需再用到
        print(name,time.ctime())
        time.sleep(1)

def run2(name,n):
    for _ in range(n):
        print(name,time.ctime())
        time.sleep(1)

if __name__ == '__main__':
    """
    设置子线程t1为守护线程,主线程不等t1运行结束,只要其他的子线程t2运行结果,就会关闭主线程。
    """
    t1 = threading.Thread(target=run1,args=("线程1",10,))   #注意args参数类型是元组并以“,”结尾
    t2 = threading.Thread(target=run2,args=("线程2",5,))
    t1.setDaemon(True)  #设置t1为守护线程
    t1.start()
    t2.start()
    # t1.join()   ##设置主线程等待子线程t1运行结束


D:\github\Python\threading_study>python threading_study03.py
线程1 Thu Oct 15 11:17:08 2020
线程2 Thu Oct 15 11:17:08 2020
线程1 Thu Oct 15 11:17:09 2020
线程2 Thu Oct 15 11:17:09 2020
线程1 Thu Oct 15 11:17:10 2020
线程2 Thu Oct 15 11:17:10 2020
线程1 Thu Oct 15 11:17:11 2020
线程2 Thu Oct 15 11:17:11 2020
线程1 Thu Oct 15 11:17:12 2020
线程2 Thu Oct 15 11:17:12 2020
线程1 Thu Oct 15 11:17:13 2020

D:\github\Python\threading_study>
           

3、线程锁对象

  • acquire()    锁定,一旦线程获取了锁,随后的尝试将其阻塞,直到释放为止。任何线程都可以释放它。
  • release()    解锁

3.1、互斥锁    threading.Lock   

"""
线程锁-互斥锁
为什么要使用线程锁分析:https://blog.csdn.net/JackLiu16/article/details/81267176
互斥锁运行顺序分析:https://blog.csdn.net/weixin_40481076/article/details/101594705

"""
import threading,time

#实例化一个互斥锁对象
lock = threading.Lock()

def run():
    lock.acquire()  #获取锁
    print(threading.current_thread().getName(),time.ctime())
    time.sleep(5)
    lock.release()  #释放锁

for _ in range(10):
    t = threading.Thread(target=run)
    t.start()


D:\github\Python\threading_study>python threading_study04.py
Thread-1 Thu Oct 15 11:19:13 2020
Thread-2 Thu Oct 15 11:19:18 2020
Thread-3 Thu Oct 15 11:19:23 2020
Thread-4 Thu Oct 15 11:19:28 2020
Thread-5 Thu Oct 15 11:19:33 2020
Thread-6 Thu Oct 15 11:19:38 2020
Thread-7 Thu Oct 15 11:19:43 2020
Thread-8 Thu Oct 15 11:19:48 2020
Thread-9 Thu Oct 15 11:19:53 2020
Thread-10 Thu Oct 15 11:19:58 2020

D:\github\Python\threading_study>
           

3.2、递归锁    threading.RLock

"""
递归锁:
互斥锁如果嵌套了多个锁之后,会将自己锁死永远都出不来了。
这个时候可以使用递归锁,它相当于一个字典,记录了锁的门与锁的对应值,当开门的时候会根据对应来开锁。

"""
import threading,time

# 生成递归锁实例
lock = threading.RLock()

# run1第二道锁
def run1():
    with lock:  #在with语句中使用锁,条件和信号量
        print("run1",threading.current_thread().getName(),time.ctime())

# run2第三道锁
def run2():
    with lock:
        print("run2",threading.current_thread().getName(),time.ctime())

# run3相当于第一道锁
def run3():
    lock.acquire()
    run1()
    time.sleep(5)
    print("run3",threading.current_thread().getName(),time.ctime())
    time.sleep(5)
    run2()
    lock.release()

# 循环开启10个线程
for i in range(10):
    t = threading.Thread(target=run3)
    t.start()
    t.join()


D:\github\Python\threading_study>python threading_study05.py
run1 Thread-1 Thu Oct 15 11:20:53 2020
run3 Thread-1 Thu Oct 15 11:20:58 2020
run2 Thread-1 Thu Oct 15 11:21:03 2020
run1 Thread-2 Thu Oct 15 11:21:03 2020
run3 Thread-2 Thu Oct 15 11:21:08 2020
run2 Thread-2 Thu Oct 15 11:21:13 2020
run1 Thread-3 Thu Oct 15 11:21:13 2020
run3 Thread-3 Thu Oct 15 11:21:18 2020
run2 Thread-3 Thu Oct 15 11:21:23 2020
run1 Thread-4 Thu Oct 15 11:21:23 2020
run3 Thread-4 Thu Oct 15 11:21:28 2020
run2 Thread-4 Thu Oct 15 11:21:33 2020
run1 Thread-5 Thu Oct 15 11:21:33 2020
run3 Thread-5 Thu Oct 15 11:21:38 2020
run2 Thread-5 Thu Oct 15 11:21:43 2020
run1 Thread-6 Thu Oct 15 11:21:43 2020
run3 Thread-6 Thu Oct 15 11:21:48 2020
run2 Thread-6 Thu Oct 15 11:21:53 2020
run1 Thread-7 Thu Oct 15 11:21:53 2020
run3 Thread-7 Thu Oct 15 11:21:58 2020
run2 Thread-7 Thu Oct 15 11:22:03 2020
run1 Thread-8 Thu Oct 15 11:22:03 2020
run3 Thread-8 Thu Oct 15 11:22:08 2020
run2 Thread-8 Thu Oct 15 11:22:13 2020
run1 Thread-9 Thu Oct 15 11:22:13 2020
run3 Thread-9 Thu Oct 15 11:22:18 2020
run2 Thread-9 Thu Oct 15 11:22:23 2020
run1 Thread-10 Thu Oct 15 11:22:23 2020
run3 Thread-10 Thu Oct 15 11:22:28 2020
run2 Thread-10 Thu Oct 15 11:22:33 2020

D:\github\Python\threading_study>
           

4、条件对象    threading.Condition()

  • 可以把Condiftion理解为一把高级的琐,它提供了比Lock, RLock更高级的功能,允许我们能够控制复杂的线程同步问题。

5、信号量对象    threading.Semaphore()

  • Semaphore()    内部计数器
  • BoundedSemaphore()    继承Semaphore类,设定信号量边界值
"""
信号量(BoundedSemaphore类):
信号量通常用于保护容量有限的资源
信号量管理一个内部计数器,该内部计数器随每个acquire()调用而递减,并随每个 调用而递增release()。
计数器永远不能低于零。当acquire() 发现它为零时,它将阻塞,直到其他线程调用为止 release()。
参考地址:https://www.cnblogs.com/chengd/articles/7770898.html

"""

import threading,time

#设置信号量的边界值
semaphore = threading.BoundedSemaphore(value=3)

def run():
    semaphore.acquire()   #加锁
    print(threading.current_thread().getName(),time.ctime())
    time.sleep(5)
    semaphore.release()    #释放

if __name__ == '__main__':
    for _ in range(10):
        t = threading.Thread(target=run)
        t.start()
    #返回Thread当前所有活动对象的列表。通过运行结果我们可以看出系统是先一次性创建10个线程,然后根据信号量边界值3,一次性运行3个线程,其他线程等待锁。
    print(threading.enumerate())


"""
python多线程的缺陷:
1、GIL-全局解释器锁
2、无论系统CPU是几核的,只能使用一个来处理进程
https://www.cnblogs.com/xiangsikai/p/8178729.html

"""



D:\github\Python\threading_study>python threading_study06.py
Thread-1 Thu Oct 15 11:23:17 2020
Thread-2 Thu Oct 15 11:23:17 2020
Thread-3 Thu Oct 15 11:23:17 2020
[<_MainThread(MainThread, started 13328)>, 
<Thread(Thread-1, started 14328)>, <Thread(Thread-2, started 15192)>, 
<Thread(Thread-3, started 14300)>, <Thread(Thread-4, started 15620)>,
<Thread(Thread-5, started 13720)>, <Thread(Thread-6, started 12456)>, 
<Thread(Thread-7, started 9652)>, <Thread(Thread-8, started 15724)>,
 <Thread(Thread-9, started 14172)>, <Thread(Thread-10, started 4132)>]
Thread-4 Thu Oct 15 11:23:22 2020
Thread-5 Thu Oct 15 11:23:22 2020
Thread-6 Thu Oct 15 11:23:22 2020
Thread-7 Thu Oct 15 11:23:27 2020
Thread-9 Thu Oct 15 11:23:27 2020
Thread-8 Thu Oct 15 11:23:27 2020
Thread-10 Thu Oct 15 11:23:32 2020

D:\github\Python\threading_study>
           

6、事件对象 threading.Event

  • set()        将“Flag”设置为True
  • clear()    将“Flag”设置为False
  • wait(timeout=None)    如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞;如果“Flag”值为True,那么执行event.wait 方法时便不再阻塞。
  • is_set()    判断“Flag”值是否为True。
"""
事件对象 threading.Event
set()	将“Flag”设置为True
clear()	将“Flag”设置为False
wait()	如果“Flag”值为 False,主线程就会阻塞;如果“Flag”值为True,主线程不再阻塞。
isSet() 判断“Flag”值是否为True。

"""

import threading
import time

#实例化一个事件对象
event = threading.Event()

def run():
    while not event.isSet():
        print(threading.current_thread().getName(), time.ctime())
        time.sleep(5)
    event.wait(timeout=10)  # 阻塞线程,为什么timeout=10没起到作用

if __name__ == '__main__':
    #使用多线程去调用run
    for i in range(10):
        th = threading.Thread(target=run)
        th.start()
    #阻塞30s后运行主线程
    time.sleep(30)
    event.set()





D:\github\Python\threading_study>python threading_study07.py
Thread-1 Thu Oct 15 11:25:08 2020
Thread-2 Thu Oct 15 11:25:08 2020
Thread-3 Thu Oct 15 11:25:08 2020
Thread-4 Thu Oct 15 11:25:08 2020
Thread-5 Thu Oct 15 11:25:08 2020
Thread-6 Thu Oct 15 11:25:08 2020
Thread-7 Thu Oct 15 11:25:08 2020
Thread-8 Thu Oct 15 11:25:08 2020
Thread-9 Thu Oct 15 11:25:08 2020
Thread-10 Thu Oct 15 11:25:08 2020
Thread-1 Thu Oct 15 11:25:13 2020
Thread-2 Thu Oct 15 11:25:13 2020
Thread-4 Thu Oct 15 11:25:13 2020
Thread-6 Thu Oct 15 11:25:13 2020
Thread-3 Thu Oct 15 11:25:13 2020
Thread-8 Thu Oct 15 11:25:13 2020
Thread-5 Thu Oct 15 11:25:13 2020
Thread-9 Thu Oct 15 11:25:13 2020
Thread-7 Thu Oct 15 11:25:13 2020
Thread-10 Thu Oct 15 11:25:13 2020
Thread-1 Thu Oct 15 11:25:18 2020
Thread-2 Thu Oct 15 11:25:18 2020
Thread-4 Thu Oct 15 11:25:18 2020
Thread-6 Thu Oct 15 11:25:18 2020
Thread-3 Thu Oct 15 11:25:18 2020
Thread-8 Thu Oct 15 11:25:18 2020
Thread-5 Thu Oct 15 11:25:18 2020
Thread-9 Thu Oct 15 11:25:18 2020
Thread-7 Thu Oct 15 11:25:18 2020
Thread-10 Thu Oct 15 11:25:18 2020
Thread-1 Thu Oct 15 11:25:23 2020
Thread-2 Thu Oct 15 11:25:23 2020
Thread-4 Thu Oct 15 11:25:23 2020
Thread-6 Thu Oct 15 11:25:23 2020
Thread-3 Thu Oct 15 11:25:23 2020
Thread-5 Thu Oct 15 11:25:23 2020
Thread-8 Thu Oct 15 11:25:23 2020
Thread-9 Thu Oct 15 11:25:23 2020
Thread-7 Thu Oct 15 11:25:23 2020
Thread-10 Thu Oct 15 11:25:23 2020
Thread-1 Thu Oct 15 11:25:28 2020
Thread-2 Thu Oct 15 11:25:28 2020
Thread-4 Thu Oct 15 11:25:28 2020
Thread-6 Thu Oct 15 11:25:28 2020
Thread-3 Thu Oct 15 11:25:28 2020
Thread-5 Thu Oct 15 11:25:28 2020
Thread-8 Thu Oct 15 11:25:28 2020
Thread-9 Thu Oct 15 11:25:28 2020
Thread-7 Thu Oct 15 11:25:28 2020
Thread-10 Thu Oct 15 11:25:28 2020
Thread-1 Thu Oct 15 11:25:33 2020
Thread-2 Thu Oct 15 11:25:33 2020
Thread-4 Thu Oct 15 11:25:33 2020
Thread-6 Thu Oct 15 11:25:33 2020
Thread-5 Thu Oct 15 11:25:33 2020
Thread-3 Thu Oct 15 11:25:33 2020
Thread-8 Thu Oct 15 11:25:33 2020
Thread-7 Thu Oct 15 11:25:33 2020
Thread-9 Thu Oct 15 11:25:33 2020
Thread-10 Thu Oct 15 11:25:33 2020
Thread-1 Thu Oct 15 11:25:38 2020

D:\github\Python\threading_study>
           

7、Timer计时器对象    threading.Timer

  • 参数:(self, interval, function, args=None, kwargs=None)
  • start()     启动计时器
  • cancel()    停止计时器(在其动作开始之前)
"""
Timer对象

"""
import threading

#实例化一个计时器
timer = threading.Timer

def hello():
    print("hello, world")

#10s后执行hello
t = timer(10, hello)
t.start()




D:\github\Python\threading_study>python threading_study08.py
hello, world

D:\github\Python\threading_study>