+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2019-07(7)

2019-08(100)

2019-09(109)

2019-10(15)

2019-11(7)

2019-12(10)

2020-01(31)

2020-02(21)

Python多线程

发布于2020-02-10 17:29     阅读(933)     评论(0)     点赞(7)     收藏(0)


首先我们区分一下线程和进程:

进程和线程的关系:

(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。

(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。

(3)处理机分给线程,即真正在处理机上运行的是线程

(4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

进程与线程的区别:

(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位

(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行

(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.

(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

多线程类似于同时执行多个不同程序,多线程运行有如下优点:

使用线程可以把占据长时间的程序中的任务放到后台去处理。

用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。

程序的运行速度可能加快。

在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
这里说明一下thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 “_thread”。
Python创建Thread对象语法如下:
    import threading
    threading.Thread(target=None, name=None,  args=())
  • 1
  • 2
主要参数说明:
target 是函数名字,需要调用的函数。
name 设置线程名字。
args 函数需要的参数,以元祖( tuple)的形式传入
Thread 对象主要方法说明:
run(): 用以表示线程活动的方法。
start():启动线程活动。
join(): 等待至线程中止。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
Python中实现多线程有两种方式:函数创建线程和创建线程类
函数创建线程:
import threading
import time, random, math


# idx 循环次数
def printNum(idx):
    for num in range(idx):
        # 打印当前运行的线程名字
        print("{0}\tnum={1}".format(threading.current_thread().getName(), num))
        # 随机生成一个数*2,向上取整使线程休眠,这里*2是为了时间间隔大一些
        delay = math.ceil(random.random() * 2)
        #进行休眠
        time.sleep(delay)


if __name__ == '__main__':
    #这里args()的值就是printNum函数的参数
    th1 = threading.Thread(target=printNum, args=(2,), name="thread1")
    th2 = threading.Thread(target=printNum, args=(3,), name="thread2")

    # 启动2个线程
    th1.start()
    th2.start()

    # 等待至线程中止
    th1.join()
    th2.join()
    print("{0} 线程结束".format(threading.current_thread().getName()))
  • 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
创建线程的时候,只需要传入一个执行函数和函数的参数即可完成threading.Thread实例的创建。
运行结果:
thread1 num=0
thread2 num=0
thread1 num=1
thread2 num=1
thread2 num=2
MainThread 线程结束
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
创建线程类:
import threading
import time, random, math

#继承Thread类
class MutliThread(threading.Thread):
    def __init__(self, threadName, num):
        threading.Thread.__init__(self)
        self.name = threadName
        self.num = num
    #重写run方法
    def run(self):
        for i in range(self.num):
            print("{0} i={1}".format(threading.current_thread().getName(), i))
            delay = math.ceil(random.random() * 2)
            time.sleep(delay)


if __name__ == '__main__':
    thr1 = MutliThread("thread1", 3)
    thr2 = MutliThread("thread2", 2)

    # 启动线程
    thr1.start()
    thr2.start()

    # 等待至线程中止
    thr1.join()
    thr2.join()
    print("{0} 线程结束".format(threading.current_thread().getName()))
  • 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
直接创建threading.Thread的子类来创建一个线程对象,实现多线程。通过继承Thread类,并重写Thread类的run()方法,在run()方法中定义具体要执行的任务。
运行结果:
thread1 i=0
thread2 i=0
thread1 i=1
thread2 i=1
thread1 i=2
MainThread 线程结束
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
守护线程:
上例中的join方法的作用是阻塞,等待子线程结束,join方法有一个参数是timeout,即如果主线程等待timeout,子线程还没有结束,则主线程强制结束子线程。简单来说就是使主线程等待子线程结束后再结束。如果子线程不使用join()函数,主线程和子线程是并行运行的,没有依赖关系,主线程执行了,子线程也在执行。
在多线程开发中,如果子线程设定为了守护线程,守护线程会等待主线程运行完毕后被销毁。一个主线程可以设置多个守护线程,守护线程运行的前提是,主线程必须存在,如果主线程不存在了,守护线程会被销毁。
import threading, time

def run(taskName):
    print("任务:", taskName)
    time.sleep(2)
    print("{0} 任务执行完毕".format(taskName))  # 查看每个子线程

if __name__ == '__main__':
    start_time = time.time()
    for i in range(3):
        thr = threading.Thread(target=run, args=("task-{0}".format(i),))
        thr.start()

    # 查看主线程和当前活动的所有线程数,active_count方法是查看当前线程数
    print("{0}线程结束,当线程数量={1}".format(threading.current_thread().getName(), threading.active_count()))
    print("消耗时间:", time.time() - start_time)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
运行结果:
    任务: task-0
    任务: task-1
    任务: task-2
    MainThread线程结束,当线程数量=4
    消耗时间: 0.0009751319885253906
    task-2 任务执行完毕
    task-0 任务执行完毕
    task-1 任务执行完毕
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
从返回结果可以看出,当前的线程个数是4,线程个数=主线程数 + 子线程数,在本例中有1个主线程和3个子线程。主线程执行完毕后,等待子线程执行完毕,程序才会退出。
在这里我们把所有的子线程都设置为守护线程。子线程变成守护线程后,只要主线程执行完毕,程序不管子线程有没有执行完毕,程序都会退出。使用线程对象的setDaemon(True)函数来设置守护线程。
import threading, time

def run(taskName):
    print("任务:", taskName)
    time.sleep(2)
    print("{0} 任务执行完毕".format(taskName))

if __name__ == '__main__':
    start_time = time.time()
    for i in range(3):
        thr = threading.Thread(target=run, args=("task-{0}".format(i),))

        # 把子线程设置为守护线程,一定在启动线程前设置
        thr.setDaemon(True)
        thr.start()

    # 查看主线程和当前活动的所有线程数
    thrName = threading.current_thread().getName()
    thrCount = threading.active_count()
    print("{0}线程结束,当线程数量={1}".format(thrName, thrCount))
    print("消耗时间:", time.time() - start_time)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
运行结果:
    任务: task-0
    任务: task-1
    任务: task-2
    MainThread线程结束,当线程数量=4
    消耗时间: 0.0010023117065429688
  • 1
  • 2
  • 3
  • 4
  • 5
这里就可以看出,设置了守护线程以后当主线程结束了,子线程也随之销毁。

线程互斥锁同步线程

import threading

balance = 100

def change(num, counter):
    global balance
    for i in range(counter):
        balance += num
        balance -= num
        if balance != 100:
            # 如果输出这句话,说明线程不安全
            print("balance=%d" % balance)
            break

if __name__ == "__main__":
    thr1 = threading.Thread(target=change, args=(100, 500000), name='t1')
    thr2 = threading.Thread(target=change, args=(100, 500000), name='t2')
    thr1.start()
    thr2.start()
    thr1.join()
    thr2.join()
    print("{0} 线程结束".format(threading.current_thread().getName()))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
运行结果:
    balance=200
    MainThread 线程结束
  • 1
  • 2
本例中在for循环的过程中,由于线程同时进行,我们定义的全局变量balance也同时被调用,只要循环次数足够,一定会有一次在t1执行balance += num后t2也执行balance += num,此时balance值为300,然后t1在进行balance -= num,此时balance值为200,进入if循环。
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire([timeout])
#释放
mutex.release()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
import threading

balance = 100

lock = threading.Lock()

def change(num, counter):
    global balance
    for i in range(counter):
        # 先要获取锁
        lock.acquire()

        balance += num
        balance -= num

        # 释放锁
        lock.release()

        if balance != 100:
            # 如果输出这句话,说明线程不安全
            print("balance=%d" % balance)
            break

if __name__ == "__main__":
    thr1 = threading.Thread(target=change,args=(100,500000),name='t1')
    thr2 = threading.Thread(target=change,args=(100,500000),name='t2')
    thr1.start()
    thr2.start()
    thr1.join()
    thr2.join()
    print("{0} 线程结束".format(threading.current_thread().getName()))
  • 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
运行结果:
MainThread 线程结束
  • 1
发布了7 篇原创文章 · 获赞 5 · 访问量 239


所属网站分类: 技术文章 > python文章

作者:what

链接: http://www.pythonheidong.com/blog/article/231020/

来源:python黑洞网 www.pythonheidong.com

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

7 0

赞一赞 or 踩一踩

收藏该文
已收藏

评论内容:(最多支持255个字符)

相似文章

  5171. 最接近的因数

  Python实现生成西瓜数据集的Excel文件

  Leetcode - 删除排序数组中的重复项

  python第五天-字符串

  数据结构|LeetCode(力扣)经典题:队列

  python学习笔记(3)

  TYD_初识python数据可视化库-Matplotlib

  Scrapy安装与应用教程

  python实现修改文件中的内容

  mac环境下python3.6安装pyhanlp工具包

优质资源排行榜

 python经典电子书大合集下载 下载次数 8138

 零基础java开发工程师视频教程全套,基础+进阶+项目实战(152G) 下载次数 7550

 零基础前端开发工程师视频教程全套,基础+进阶+项目实战(共120G) 下载次数 7442

 零基础大数据全套视频400G 下载次数 7006

 零基础php开发工程师视频教程全套,基础+进阶+项目实战(80G) 下载次数 6893

 零基础软件测试全套系统教程 下载次数 6506

 全套人工智能视频+pdf 下载次数 6443

 IOS全套视频教程 基础班+就业班 下载次数 4680

 编程小白的第一本python入门书(高清版)PDF下载 下载次数 3648

10  effective python编写高质量Python代码的59个有效方法 pdf下载 下载次数 3361

11  Python深度学习 pdf下载 下载次数 3156

12  笨办法学python pdf下载 下载次数 3087

13  Python Cookbook第三版中文PDF下载高清完整扫描原版 下载次数 3024

14  树莓派Python编程指南 pdf下载 下载次数 3011

15  python从入门到精通视频(全60集)python视频教程下载 下载次数 3009

16  python项目开发视频 下载次数 3002

17  使用python+pygame开发的小游戏《嗷大喵快跑》源码下载 下载次数 3000

18  黑马2017年java就业班全套视频教程 下载次数 2992

19  Python基础教程 pdf下载 下载次数 2988

20  python实战项目 平铺图像板系统源码下载,适用于想要保存,标记和共享图像,视频和网页的用户 下载次数 2987

21  利用python实现程序内存监控脚本 下载次数 2987

22  老男孩python自动化视频 下载次数 2983

23  老王python基础+进阶+项目视频教程 下载次数 2974

24  尚硅谷Go学科全套视频 下载次数 2972

25  某硅谷Python项目+AI课程+核心基础视频教程 下载次数 2968

26  Web前端实战精品课程 下载次数 2967

27  Python算法教程_中文版 pdf下载 下载次数 2966

28  tron python小游戏 下载次数 2963

29  [小甲鱼]零基础入门学习Python 下载次数 2962

30  老男孩python全栈开发15期 下载次数 2958

31  2017最新web前端开发完整视频教程附源码 下载次数 2948

32  最新全套完整JAVAWEB2018开发视频 下载次数 2926

33  流畅的Python PDF下载高清完整扫描原版 下载次数 2919

34  Spring boot实战视频6套下载 下载次数 2910

35  python全套视频十五期(116G) 下载次数 2908

36  Python高性能编程 pdf下载 下载次数 2906

37  Python项目实战 下载次数 2887

38  30个小时搞定Python网络爬虫 含源码 下载次数 2884

39  简明python教程 (A Byte of Python)pdf下载 下载次数 2884

40  利用Python进行数据分析 pdf下载 下载次数 2884

41  python全自动抢火车票教程-python视频教程下载 下载次数 2883

42  尚硅谷大数据之Hadoop视频 下载次数 2876

43  Python A~B~C~ python视频教程下载 下载次数 2866

44  数据结构与算法视频(小甲鱼讲解-全) 下载次数 2864

45  web小程序表白天数倒计时源码下载 下载次数 2863

46  python基础视频教程 下载次数 2862

47  go语言全套视频 下载次数 2855

48  清华学霸尹成Python爬虫视频-ok 下载次数 2846

49  黑马前端36期最全视频和代码 下载次数 2843

50  2018最新全套web前端视频教程+源码下载 下载次数 2841