python 线程与进程学习(2)----多线程

前言

    在这里记录一下我学习多线程的一些笔记

1.多线程简介

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

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理.
  • 用户界面可以更加吸引人
  • 程序运行的时间可能加快
  • 在一些等待的任务的实现中如:用户输入,文件读写和网络收发数据等,线程就比较有用了.

注:主程序是主线程,程序中创建的线程都是子线程

2.python实现多线程

    我们来通过一个简单的程序来学习一下多线程.
    先来看一个不用多线程的程序:test_1.py:

import time

def func(n):
    start_time = time.time()
    print('task',n)
    time.sleep(2)
    end_time = time.time()
    print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))

if __name__ == '__main__':
    start_time = time.time()
    for i in range(2):
        func(i+1)

    end_time = time.time()

    print('程序所花时间为:',end_time-start_time)

    执行结果:

task 1
任务1所用时间为:2.0012800693511963
task 2
任务2所用时间为:2.0009312629699707
程序所花时间为: 4.002368211746216

    可以看出,每个任务都花费2秒左右的时间,程序总共话费时间为4秒左右.这个我们也很好理解,因为程序是串行的,只有第一个任务完成后才能完成第二个任务.

    接下来我们看一下使用多线程后的效果:threading_1.py

import time
import threading

def func(n):
    start_time = time.time()
    print('task',n)
    time.sleep(2)
    end_time = time.time()
    print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))

if __name__ == '__main__':
    start_time = time.time()

    for i in range(2):
        t = threading.Thread(target=func,args=(i+1,))
        t.start()
        
    end_time = time.time()

    print('程序所花时间为:',end_time-start_time)

    执行结果:

task 1
task 2
程序所花时间为: 0.0005745887756347656
任务2所用时间为:2.002145528793335
任务1所用时间为:2.0024964809417725

我们惊讶的发现,竟然是主程序执行完成以后又执行了两个子程序.为什么会出现这种情况呢?
出现这种情况的原因就是:每个线程都是独立且[平等的,python执行多线程的时候其实是按着上下文切换的原则来执行每个线程的,当遇到IO操作时,会自动切换另一个线程.在该程序中,当task 1和task 2执行完print()操作后遇到了time.sleep(2)操作,所以此时就会切换到主线程,主线程一路畅通,执行到最后(主线程也就是主函数),当线程执行完成后,再回去执行没有执行完的线程.

那么我们能不能让所有的子线程都执行完后再执行主线程呢?答案是可以的,我们来看下面这段程序.

import time
import threading

def func(n):
    start_time = time.time()
    print('task',n)
    time.sleep(2)
    end_time = time.time()
    print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))

if __name__ == '__main__':
    start_time = time.time()

    thread_list = []

    for i in range(2):
        t = threading.Thread(target=func,args=(i+1,))
        t.start()
        thread_list.append(t)

    for t in thread_list:
        t.join()

    end_time = time.time()

    print('程序所花时间为:',end_time-start_time)

    执行结果:

task 1
task 2
任务2所用时间为:2.0020792484283447
任务1所用时间为:2.002394437789917
程序所花时间为: 2.0028533935546875

我们可以看到,此时就可以实现并发的效果了.每个子线程执行时间为2秒,程序都执行完后也是2秒.

    程序解释:
(1)我们要实现多线程,首先要导入python多线程模块:threading

import threading

(2)创建一个函数,用于测试多线程:

def func(n):
    start_time = time.time()
    print('task',n)
    time.sleep(2)
    end_time = time.time()
    print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))

    这个函数我们就不做过多解释了.
(3)创建线程对象:

t = threading.Thread(target=func,args=(i+1,))

注:创建多线程时使用threading.Thread(),其中,target是要运行的函数,不能加括号,args是要传入的参数,这里的args接收的是一个元组,所以,我们即便是只有一个参数,也要加上逗号.

(4)启动线程

t.start()

(5)等待子线程终止

t.join()

(6)等待主线程执行完.

如果看不出多线程的效果,我们来看下面这个例子.

import time
import threading

def func(n):
    start_time = time.time()
    print('task',n)
    time.sleep(2)
    end_time = time.time()
    print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))

if __name__ == '__main__':
    start_time = time.time()

    thread_list = []

    for i in range(20):
        t = threading.Thread(target=func,args=(i+1,))
        t.start()
        thread_list.append(t)

    for t in thread_list:
        t.join()


    end_time = time.time()

    print('程序所花时间为:',end_time-start_time)

    我们启动了20个子任务,如果按照串行的方式来运行程序,每个子任务需要2秒,20个子任务需要40秒完成,但我们使用了多线程后,可以实现40个子任务并行的效果,最终只需要2秒就可以完成任务,来看执行结果:

task 1
task 2
task 3
task 4
task 5
task 6
task 7
task 8
task 9
task 10
task 11
task 12
task 13
task 14
task 15
task 16
task 17
task 18
task 19
task 20
任务1所用时间为:2.0002286434173584
任务4所用时间为:2.000516653060913
任务6所用时间为:2.001020669937134
任务11所用时间为:2.0004289150238037
任务12所用时间为:2.0003764629364014
任务3所用时间为:2.0015926361083984
任务5所用时间为:2.001516819000244
任务9所用时间为:2.0010743141174316
任务7所用时间为:2.001377582550049
任务8所用时间为:2.0015039443969727
任务2所用时间为:2.0023436546325684
任务15所用时间为:2.0006370544433594
任务14所用时间为:2.000804901123047
任务10所用时间为:2.001779317855835
任务20所用时间为:2.000225782394409
任务16所用时间为:2.001055955886841
任务13所用时间为:2.0022807121276855
任务17所用时间为:2.0021753311157227
任务19所用时间为:2.0018365383148193
任务18所用时间为:2.0020861625671387
程序所花时间为: 2.0048460960388184

可能会有人问,既然 t.join()是等待子线程结束,那为什么不 t.start()后直接 t.join()呢?
我们来看下面的程序:

import threading
import time

# 定义函数,用来测试多线程
def func(n):
    start_time = time.time()
    print('task',n)
    time.sleep(2)
    end_time = time.time()
    print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))

class MyThread(threading.Thread):                           # 创建自己的多线程类,继承threading.Thread
    def __init__(self,thread_id,thread_name,count):
        super(MyThread, self).__init__()                    # 重构父类__init__()函数,要继承父类
        self.thread_id = thread_id
        self.thread_name = thread_name
        self.count = count

    def run(self):                                          # 要将线程执行的任务写在run方法中.
        print('开始线程:',self.name)
        func(self.count)
        print('退出线程:',self.name)

if __name__ == '__main__':
    start_time = time.time()
    for i in range(6):
        t = MyThread(i,'thread-'+str(i),i+1)                # 实例化线程
        t.start()                                           # 启动线程
        t.join()
       
    end_time = time.time()
    print('程序所用时间为:',end_time-start_time)

直接来看结果:

开始线程: Thread-1
task 1
任务1所用时间为:2.002120018005371
退出线程: Thread-1
开始线程: Thread-2
task 2
任务2所用时间为:2.0021109580993652
退出线程: Thread-2
开始线程: Thread-3
task 3
任务3所用时间为:2.002117872238159
退出线程: Thread-3
开始线程: Thread-4
task 4
任务4所用时间为:2.0021157264709473
退出线程: Thread-4
开始线程: Thread-5
task 5
任务5所用时间为:2.00205659866333
退出线程: Thread-5
开始线程: Thread-6
task 6
任务6所用时间为:2.0020573139190674
退出线程: Thread-6
程序所用时间为: 12.01600456237793

可以看到,这样的效果其实就是线程串行,这样就失去了多线程的意义.

3.使用类创建多线程

    我们还可以使用创建类的形式创建多线程.

import threading
import time

# 定义函数,用来测试多线程
def func(n):
    start_time = time.time()
    print('task',n)
    time.sleep(2)
    end_time = time.time()
    print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))

class MyThread(threading.Thread):                           # 创建自己的多线程类,继承threading.Thread
    def __init__(self,thread_id,thread_name,count):
        super(MyThread, self).__init__()                    # 重构父类__init__()函数,要继承父类
        self.thread_id = thread_id
        self.thread_name = thread_name
        self.count = count

    def run(self):                                          # 要将线程执行的任务写在run方法中.
        print('开始线程:',self.name)
        func(self.count)
        print('退出线程:',self.name)

if __name__ == '__main__':
    start_time = time.time()
    thread_list = []
    for i in range(6):
        t = MyThread(i,'thread-'+str(i),i+1)                # 实例化线程
        t.start()                                           # 启动线程
        thread_list.append(t)

    for t in thread_list:
        t.join()                                            # 等待子线程终止

    end_time = time.time()
    print('程序所用时间为:',end_time-start_time)

    执行结果:

开始线程: Thread-1
开始线程: Thread-2
task 1
task 2
开始线程: Thread-3
task 3
开始线程: Thread-4
task 4
开始线程: Thread-5
task 5
开始线程: Thread-6
task 6
任务2所用时间为:2.0006625652313232
退出线程: Thread-2
任务1所用时间为:2.002107858657837
退出线程: Thread-1
任务3所用时间为:2.0011298656463623
退出线程: Thread-3
任务6所用时间为:2.0014195442199707
任务4所用时间为:2.0017075538635254
退出线程: Thread-4
退出线程: Thread-6
任务5所用时间为:2.002039670944214
退出线程: Thread-5
程序所用时间为: 2.0067389011383057

使用类创建多线程时要注意以下几点:

  • 要继承threading.Thread类
  • 要将多线程执行的过程写在run()方法中.

4.多线程函数总结

    我们在之前用到了多线程的 start()函数,join()函数以及run()函数,这里我们就来总结一下多线程中的函数.
(1)创建线程 threading.Thread:

t = threading.Thread(target = func,args = (n,))

(2)启动线程:

t.start()

(3)阻塞子线程,等待子线程结束后再往下执行:

t.join()

(4)run()和start()的区别:
    start()方法是启动一个子线程
    run()方法并不启动一个新线程,只是在主线程中调用了一个普通函数而已.
    t.start()的时候实际上是执行的run()函数中的流程.
(5)返回当前的线程变量:

threading.current_thread()

(6)返回正在运行的线程数量:

threading.active_count()
写在最后

    本文是个人的一些学习笔记,如有侵权,请及时联系我进行删除,谢谢大家.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值