原文:https://www.cnblogs.com/i-honey/p/7823587.html

Python中实现多线程需要使用到 threading 库,其中每一个 Thread类 的实例控制一个线程。

Thread类

#类签名

1
2
def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, *, daemon=None):

  简单介绍一些初始化参数:

target: 指定线程由 run () 方法调用的可调用对象。默认为 None, 意味着不调用任何内容。

name: 指定该线程的名称。 在默认情况下,创建一个唯一的名称。

args: target调用的实参,元组格式。默认为 (),即不传参。

daemon: 为False表示父线程在运行结束时需要等待子线程结束才能结束程序,为True则表示父线程在运行结束时,子线程无论是否还有任务未完成都会跟随父进程退出,结束程序。

线程启动:

1
2
3
4
5
6
7
8
9
10
11
12
13
import threading
 
 
def worker(arg):#线程执行的目标函数
    print("I'm working {}".format(arg))
    print("Fineshed")
 
t = threading.Thread(target=worker,args=(threading.current_thread(),),name="firstworker")#线程对象
t.start()#启动线程
 
运行结果:
I'm working <_MainThread(MainThread, started 10936)>
Fineshed

  上面例子中,当函数执行完之后,线程也就跟着退出了。

线程的传参:

1
2
3
4
5
6
7
8
9
10
11
12
import threading
 
def add(x,y):
    print(x+y)
 
t = threading.Thread(target=add,args=(4,5))
t.start()
 
print("====end===")
运行结果:
9
====end===

  线程的传参和函数传参没有区别,只需要注意传入的必须为元祖格式。

线程退出:

如果线程中任务是无限循环语句,那这个线程将无法自动停止。

Python线程退出条件有以下几种:

1、线程内的函数语句执行完毕,线程自动结束

2、线程内的函数抛出未处理的异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import threading
import time
 
def worker(arg):
    while True:
        time.sleep(1)
        print("I'm working {}".format(arg))
    print("Fineshed")
 
t = threading.Thread(target=worker,args=(threading.current_thread(),),name="firstworker")
t.start()
运行结果:
I'm working <_MainThread(MainThread, stopped 2468)>
I'm working <_MainThread(MainThread, stopped 2468)>
I'm working <_MainThread(MainThread, stopped 2468)>
...

  上面例子中,线程启动后,将一直循环下去,线程不会自动退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import threading
import time
 
def worker(arg):
    count = 0
    while True:
        if count > 5:
            raise RuntimeError(count)
        time.sleep(1)
        print("I'm working {}".format(arg))
        count += 1
    print("Fineshed")
 
t = threading.Thread(target=worker,args=(threading.enumerate(),))
t.start()
 
print("====end===")
 
运行结果:
====end===
I'm working [<_MainThread(MainThread, stopped 10992)>]
I'm working [<_MainThread(MainThread, stopped 10992)>]
I'm working [<_MainThread(MainThread, stopped 10992)>]
I'm working [<_MainThread(MainThread, stopped 10992)>]
I'm working [<_MainThread(MainThread, stopped 10992)>]
I'm working [<_MainThread(MainThread, stopped 10992)>]
Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:/python/test.py", line 8, in worker
    raise RuntimeError(count)
RuntimeError: 6

  上面例子中,演示了触发异常自动退出线程。但最先打印的是主程序的"===end==="语句,是因为在程序中,主线程启动一个线程后,不会等待子线程执行完毕,就继续执行了后续语句,在执行完主线程语句后,发现还有子线程没有结束,于是等待子线程执行结束,子线程在运行时抛出了未处理的异常,最终子线程结束,主线程也随之结束。这里需要了解daemon线程和non-daemon线程,稍后就会介绍。

threading属性:

threading.current_thread()   返回当前线程对象
threading.main_thread() 返回主线程对象
threading.active_count() 返回处于Active状态的线程个数
threading.enumerate() 返回所有存活的线程的列表,不包括已经终止的线程和未启动的线程
threading.get_ident() 返回当前线程的ID,非0整数

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import threading
import time
 
def showthreadinfo():
    print("current thread = {}".format(threading.current_thread()))
    print("main thread  = {}".format(threading.main_thread()))
    print("active thread count = {}".format(threading.active_count()))
    print("active thread list = {}".format(threading.enumerate()))
    print("thread id = {}".format(threading.get_ident()))
    print("~~~~~~~~~~~~~")
 
def add(x,y):
    time.sleep(1)
    showthreadinfo() #子线程中调用
    print(x+y)
 
showthreadinfo() #主线程中调用
time.sleep(1)
 
t = threading.Thread(target=add,args=(4,5))
t.start()
 
print("====end===")
 
运行结果:
current thread = <_MainThread(MainThread, started 192)>
main thread  = <_MainThread(MainThread, started 192)>
active thread count = 1
active thread list = [<_MainThread(MainThread, started 192)>]
thread id = 192
~~~~~~~~~~~~~
====end===
current thread = <Thread(Thread-1, started 8424)>
main thread  = <_MainThread(MainThread, stopped 192)>
active thread count = 2
active thread list = [<_MainThread(MainThread, stopped 192)>, <Thread(Thread-1, started 8424)>]
thread id = 8424
~~~~~~~~~~~~~
9

  上面例子中,在主线程中只能看到存活的只有自己,因为子线程还没有启动,且它的父线程就是它自己。子线程启动时,它的名字为Thread-1,这个名字是解释器自动命名的,如果定义线程对象时添加了name="threadName",则这里显示的就是threadName;同时,子线程的父线程就是主线程,也就是说谁启动的线程谁就是它的父线程;子线程能看到的存活线程有父线程和自身。

Thread实例的属性:

threading.current_thread().name        线程名,只是一个标识符,可以使用getName()、setName()获取和运行时重命名。
threading.current_thread().ident 线程ID,非0整数。线程启动后才会有ID,否则为None。线程退出,此ID依旧可以访问。此ID可以重复使用
threading.current_thread().is_alive() 返回线程是否存活,布尔值,True或False。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import threading
import time
 
def worker():
    count = 1
    while True:
        if count >= 6:
            break
        time.sleep(1)
        count += 1
        print("thread name = {}".format(threading.current_thread().name))
 
t = threading.Thread(target=worker,name="MyThread")
t.start()
 
while True:
    time.sleep(1.1)
    if t.is_alive():
        print("{} {} alive".format(t.name,t.ident))
    else:
        print("{} {} alive".format(t.name, t.ident))
        t.start()
 
print("====end===")
 
运行结果:
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
Traceback (most recent call last):
  File "C:/python/test.py", line 22, in <module>
    t.start()
    raise RuntimeError("threads can only be started once")
RuntimeError: threads can only be started once

  从上面例子中可以看到子线程存活时的名字和线程ID,但在线程退出后,尝试再次启动线程时,抛出RuntimeError异常,表明线程对象在定义后只能启动一次。

举例 getName()和setName():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import threading
import time
 
def add(x,y):
    for _ in range(5):
        time.sleep(1)
        print("x+y={}".format(x+y))
 
t = threading.Thread(target=add,name="MyThread",args=(6,7))
t.start()
 
while True:
    time.sleep(1)
    if t.is_alive():
        print("{} {} alive".format(t.name,t.ident))
        print("Thread name",t.getName())
        t.setName("MyThreadTwo")
    else:
        print("{} {} alive".format(t.name, t.ident))
        print("Thread abort....")
        break
        # t.start()
 
print("====end===")
 
运行结果:
MyThread 2564 alive
Thread name MyThread
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread abort....
====end===

  上面例子演示了在运行时获取线程名和重命名线程名。

线程的start()和run()方法:

start():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import threading
import time
 
def add(x,y):
    for _ in range(5):
        time.sleep(0.5)
        print("x+y={}".format(x+y))
 
class MyThread(threading.Thread):
    def start(self):
        print('start~~~~~~~~~~')
        super().start()
 
    def run(self):
        print('run~~~~~~~~~~~~')
        super().run()  #调用父类的start()和run()方法
 
 
t = MyThread(target=add,name="MyThread",args=(6,7))
t.start()
# t.run()
print("====end===")
 
运行结果:
start~~~~~~~~~~
run~~~~~~~~~~~~
====end===
x+y=13
x+y=13
x+y=13
x+y=13
x+y=13

  从上面的例子中,可以看出start()方法会先运行start()方法,再运行run()方法。

跟进一下start() 方法源码中的调用过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1def start(self):
    _start_new_thread(self._bootstrap, ())
    ....
 
2、_start_new_thread = _thread.start_new_thread
 
3def start_new_thread(function, args, kwargs=None):
    pass
 
4def _bootstrap(self):
    self._bootstrap_inner()
 
5def _bootstrap_inner(self):
    ....
    try:
        self.run()#最终start()方法调用了run()方法
    except SystemExit:
        pass

  从上面跟踪源码的过程大概了解了start()方法如何调用到了run()方法。

run()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import threading
import time
 
def add(x,y):
    for _ in range(5):
        time.sleep(0.5)
        print("x+y={}".format(x+y))
 
class MyThread(threading.Thread):
    def start(self):
        print('start~~~~~~~~~~')
        super().start()
 
    def run(self):
        print('run~~~~~~~~~~~~')
        super().run()  #调用父类的start()和run()方法
 
 
t = MyThread(target=add,name="MyThread",args=(6,7))
# t.start()
t.run()
print("====end===")
 
运行结果:
run~~~~~~~~~~~~
x+y=13
x+y=13
x+y=13
x+y=13
x+y=13
====end===

  上面例子中,运行线程的run()方法只能调用到run()方法。

跟踪一下run() 方法在源码中的调用过程:

1
2
3
4
5
6
7
8
9
10
11
1def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, *, daemon=None):
    self._target = target
    self._args = args
    self._kwargs = kwargs
    ....
 
2def run(self):
    if self._target:
        self._target(*self._args, **self._kwargs)
    ....

  可以看出,_target是我们传入的目标函数,run()方法其实就类似一个装饰器,最终还是将_args 和_kwargs 参数传入目标函数运行,返回结果。

start() --> run() --> _target()

run() --> _target()

上面两个例子简单介绍了start()方法和run()方法的调用,下一篇文章再详细看一下它们到底有什么区别。

总结:

本文主要介绍了: Thread类、线程启动、线程的传参、线程退出、threading属性、Thread实例的属性、举例getName()和setName()、线程的start()和run()方法

Python 中的线程-进程2的更多相关文章

  1. Python 中的线程-进程1

    原文:https://www.cnblogs.com/i-honey/p/8042047.html 1. 并行和并发 并行:同时做某些事,可以互不干扰的同一时刻做几件事. 并发:也是同时做某些事,但是 ...

  2. Python中的线程和进程

    引入进程和线程的概念及区别 threading模块提供的类:   Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, l ...

  3. python中的线程技术

    #!/user/bin/env python # @Time :2018/7/7 11:42 # @Author :PGIDYSQ #@File :DaemonTest.py import threa ...

  4. 理解 Python 中的线程

    原地址:http://blog.jobbole.com/52060/ 本文由 伯乐在线 - acmerfight 翻译自 Akshar Raaj.欢迎加入技术翻译小组.转载请参见文章末尾处的要求. 我 ...

  5. 【Python】解析Python中的线程与进程

    基础知识 线程 进程 两者的区别 线程的类型 Python 多线程 GIL 创建多线程 线程合并 线程同步与互斥锁 可重入锁(递归锁) 守护线程 定时器 Python 多进程 创建多进程 多进程通信 ...

  6. Python之路-Python中的线程与进程

    一.发展背景 任务调度 大部分操作系统(如Windows.Linux)的任务调度是采用时间片轮转的抢占式调度方式,也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行.任务执行的 ...

  7. 15.python并发编程(线程--进程--协程)

    一.进程:1.定义:进程最小的资源单位,本质就是一个程序在一个数据集上的一次动态执行(运行)的过程2.组成:进程一般由程序,数据集,进程控制三部分组成:(1)程序:用来描述进程要完成哪些功能以及如何完 ...

  8. Python学习之==>线程&&进程

    一.什么是线程(thread) 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一个线程指的是进程中一个单一顺序的控制流,一个进程中可以包含多个线程,每条线程并行 ...

  9. python中的线程锁

    锁对象 原始锁是一个在锁定时不属于特定线程的同步基元组件.在Python中,它是能用的最低级的同步基元组件,由 _thread 扩展模块直接实现. 原始锁处于 "锁定" 或者 &q ...

随机推荐

  1. d3js技术文档

      D3js技术文档 概述 D3 allows you to bind arbitrary data to a Document Object Model (DOM), and then apply ...

  2. in 型子查询引出的陷阱

    题: 在ecshop商城表中,查询6号栏目的商品, (注,6号是一个大栏目) 最直观的: mysql> select goods_id,cat_id,goods_name from goods ...

  3. SpringMVC & SpringBoot小记

    SpringMVC 1.SpringMVC常用注解 https://blog.csdn.net/lipinganq/article/details/79155072 :@Component.@Serv ...

  4. 数据库 proc编程八

    #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <stri ...

  5. Python RGB 和HSV颜色相互转换

    转自:http://outofmemory.cn/code-snippet/1002/Python-RGB-HSV-color-together-switch Python RGB 和HSV颜色相互转 ...

  6. 第三百零七节,Django框架,models.py模块,数据库操作——表类容的增删改查

    Django框架,models.py模块,数据库操作——表类容的增删改查 增加数据 create()方法,增加数据 save()方法,写入数据 第一种方式 表类名称(字段=值) 需要save()方法, ...

  7. ffmpeg avformat_open_input返回失败的解决办法

    用ffmpeg做的第一个程序,参考网上的代码,就出现了一些问题,其中avformat_open_input返回失败. 下面是我在网上收集到的失败信息的相关解决: /////////////////// ...

  8. php微信开发 -- 两种运营模式及服务器配置

    微信的两种运营模式 编辑模式:使用微信公众平台提供的功能 开发者模式:通过腾讯的api接口调用相应程序进行二次开发 编辑模式 应用场景: l 不具备开发能力的运营者 l 主要是进行品牌宣传.新闻媒体. ...

  9. linux -- 修改文件

    vi编辑器有三种模式:命令模式,编辑模式,末行模式 打开vi后首先是命令模式,用i,o,a等进入编辑模式, 按esc退出编辑模式,回到命令模式. 在命令模式下输入:wq表示保存退出,:wq!强制保存退 ...

  10. 【转】StackOverflow程序员推荐:每个程序员都应读的30本书

    “如果能时光倒流,回到过去,作为一个开发人员,你可以告诉自己在职业生涯初期应该读一本,你会选择哪本书呢?我希望这个书单列表内容丰富,可以涵盖很多东西.” 很多程序员响应,他们在推荐时也写下自己的评语. ...