zoukankan      html  css  js  c++  java
  • 06_多线程

    1.线程概述

        1.线程是实现多任务编程的一种方法,可以使用计算机多核资源,是计算机核心分配的最小单位,线程由代码段,数据段,和TCB(线程控制块)组成

        2.线程又称为轻量级进程,在创建和删除时消耗的计算机资源小,理论上创建和销毁线程的消耗是创建和销毁进程消耗的二十分之一

        3.一个进程中的所有线程共享进程的空间资源(空间,全局变量,分配的内存等),进程中每个线程有自己的特有属性,如指令集TID等

        4.多线程程序的执行顺序是不确定的,主线程会等待所有的子线程结束后才结束

        5.计算机开启的线程数量建议: CPU核数 * 5

    2.threading模块语法概述

    import threading
    t = thread.Thread()  # 创建线程并返回线程对象
        参数:
            target: 线程函数
            args: 给线程函数的位置参数(类型为元组)
            kwargs: 给线程函数的字典传参(类型为字典)
            name: 给线程取名字(默认为Thread-1)
    t.start(): 启动线程
    t.join(timeout): 回收线程
    t.is_alive(): 查看线程状态
    t.name: 查看线程名称
    threading.currentThread(): 得到线程对象
    t.setName(): 设置线程名称
    t.daemon = True: 守护线程,默认为False主线程执行完毕不会影响分支线程的执行,True则表示主线程执行完毕其它线程也会终止
        设置方法: t.daemon = True 或者 t.setDaemon(True)
    t.isDaemon(True): 判断daemon属性是 True or False

    3.线程属性示例

    from threading import Thread
    from threading import currentThread
    from time import sleep
    
    
    def func(sec):
        print("线程属性测试")
        sleep(sec)
        print("%s线程结束" % currentThread().getName())
    
    
    def main():
        thread = list()
        for i in range(3):
            t = Thread(name="t-" + str(i), target=func, args=(5,))
    
            # 设置deamon属性为True,此时主线程结束,子线程也会结束
            # t.setDaemon(True)  # 默认为False,主线程等待子线程结束
            print("isDaemon", t.isDaemon())
            # 也可以在定义后设置线程名称
            # t.setName = "t-" + str(i)
            t.start()
            thread.append(t)
        for i in thread:
            i.join(1)
            print("thread name:", i.name)
            print("alive:", i.is_alive())
    
        print("主线程结束")
    
    
    if __name__ == '__main__':
        main()
    """执行结果
        isDaemon False
        线程属性测试
        isDaemon False
        线程属性测试
        isDaemon False
        线程属性测试
        thread name: t-0
        alive: True
        thread name: t-1
        alive: True
        thread name: t-2
        alive: True
        主线程结束
        t-0线程结束
        t-2线程结束
        t-1线程结束
    """

    4.线程定时器

    from threading import Timer  # 定时器
    
    
    def func():
        print('此生一入IT们,从此不爱任何人!')
    
    
    Timer(2.5, func).start()
    # Timer(time, func)
    # time: 睡眠的时间,以秒为单位
    # func: 睡眠时间之后,需要执行的任务

    5.验证守护线程

    from threading import Thread
    import time
    
    
    def func():
        time.sleep(2)
        print(123)
    
    
    def func1():
        time.sleep(1)
        print('abc')
    
    
    # 守护线程不是根据主线程的代码执行结束而结束,而是根据主线程执行结束才结束
    # 主线程会等待普通线程执行结束再结束,守护线程会等待主线程结束再结束,所以一般把不重要的事情设置为守护线程
    # 守护进程是根据主进程的代码执行完毕,守护进程就结束
    if __name__ == '__main__':
        t = Thread(target=func)
        t.daemon = True
        t.start()
        t1 = Thread(target=func1)
        t1.start()
        print(456)

    6.自定义线程函数

    # 将函数作为实参传递给线程类,通过线程类的实例化对象启动线程
    import threading
    import time
    
    
    def sing():
        """唱歌5秒钟"""
        for i in range(5):
            print("---正在唱歌---%s" % i)
            time.sleep(1)
    
    
    def dance():
        """跳舞10秒钟"""
        for i in range(10):
            print("---正在跳舞---%s" % i)
            time.sleep(1)
    
    
    def main():
        # 创建线程
        t1 = threading.Thread(target=sing)
        t2 = threading.Thread(target=dance)
        # 启动线程
        t1.start()
        t2.start()
    
        for i in range(15):
            # 查看当前线程数量
            print(threading.enumerate())
            time.sleep(1)
    
        t1.join()  # 等待回收线程
        t2.join()  # 等待回收线程
    
    
    if __name__ == "__main__":
        main()

    7.自定义线程类

        1.继承Thread类重写 run 方法

    import threading
    import time
    
    
    # threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法
    # 而创建自己的线程实例后,通过Thread类的start方法启动该线程
    class MyThread(threading.Thread):
        def run(self):
            for i in range(3):
                time.sleep(1)
                msg = "I'm "+self.name+' @ '+str(i) #name属性中保存的是当前线程的名字
                print(msg)
    
    
    if __name__ == "__main__":
        t = MyThread()
        t.start()

        2.继承Thread类重写 run 方法和 __init__ 方法

    from threading import Thread
    from time import ctime
    from time import sleep
    
    
    class MyThread(Thread):
        def __init__(self, func, args, name="Tedu"):
            super().__init__()
            self.func = func
            self.args = args
            self.name = name
    
        def run(self):
            self.func(*self.args)
    
        @staticmethod  # 声明此方法是静态方法
        def plary(file, sec):
            for _ in range(sec):
                print("%s: %s" % (file, ctime()))
                sleep(sec)
    
    
    def main():
        t = MyThread(MyThread.plary, ("echo", 3))
        t.start()
        t.join()
    
    
    if __name__ == "__main__":
        main()
    """
        echo: Sun Jul 19 23:13:55 2020
        echo: Sun Jul 19 23:13:58 2020
        echo: Sun Jul 19 23:14:01 2020
    """

    8.Python线程的全局解释器锁(GIL)

    全局解释器锁-产生的原因

            GIL是CPython解释器存在的问题
            Python支持多线程 同步互斥 加锁 超级锁 但在同一时刻解释器只能解释一个线程
            大量的Python库为了省事沿用了这种方法导致了Python多线程效率低下

    全局解释器锁-用htop命令查看CPU占有率验证

            1.单线程死循环

    def main():
        # 主线程死循环
        while True:
            pass
    
    
    if __name__ == "__main__":
        main()

            2.多线程死循环

    import threading
    
    
    # 子线程死循环
    def test():
        pass
    
    
    def main():
        t1 = threading.Thread(target=test)
        t1.start()
    
        # 主线程死循环
        while True:
            pass
    
    
    if __name__ == "__main__":
        main()

            3.多进程死循环

    import multiprocessing
    
    
    # 子进程死循环
    def test():
        pass
    
    
    def main():
        p1 = multiprocessing.Process(target=test)
        p1.start()
    
        # 主进程死循环
        while True:
            pass
    
    
    if __name__ == "__main__":
        main()

    全局解释器锁-通过计算密集耗时和IO操作耗时验证

    import time
    import threading
    import multiprocessing
    
    
    # 计算密集
    def calculation(num, x=1, y=1):
        for _ in range(num):
            x += 1
            y += 1
    
    
    # IO密集-读写
    def io_write_read(num):
        # IO密集-写
        f = open("./test.txt", "w")
        for _ in range(num):
            f.write("hello world!
    ")
        f.close()
    
        # IO密集-读
        f = open("./test.txt", "r")
        for _ in range(num):
            f.readline()
        f.close()
    
    
    # 单任务耗时
    def single_task_time(num, func):
        t_start = time.time()
        for _ in range(10):
            func(num)
        t_end = time.time() - t_start
        print("单线程CPU执行1亿次%s耗时: %s" % (func.__name__, t_end))
    
    
    # 多线程耗时
    def threading_time(num, func):
        t_start = time.time()
        counts = list()
        for _ in range(10):
            # t = threading.Thread(target=func, args=(num,), kwargs={"x": 1, "y": 1})
            t = threading.Thread(target=func, args=(num,))
            t.start()
            counts.append(t)
        for i in counts:
            i.join()
        t_end = time.time() - t_start
        print("10个线程CPU执行1亿次%s耗时: %s" % (func.__name__, t_end))
    
    
    # 多进程耗时
    def multiprocessing_time(num, func):
        t_start = time.time()
        counts = list()
        for _ in range(10):
            p = multiprocessing.Process(target=func, args=(num,))
            p.start()
            counts.append(p)
        for i in counts:
            i.join()
        t_end = time.time() - t_start
        print("10个进程CPU执行1亿次%s耗时: %s" % (func.__name__, t_end))
    
    
    def main():
        # 单线程耗时
        single_task_time(5000000, calculation)  # 单线程CPU执行1亿次calculation耗时: 5.152180910110474
        single_task_time(5000000, io_write_read)  # 单线程CPU执行1亿次io_write_read耗时: 22.120678186416626
        # 多线程耗时
        threading_time(5000000, calculation)  # 10个线程CPU执行1亿次calculation耗时: 5.205517053604126
        threading_time(5000000, io_write_read)  # 10个线程CPU执行1亿次io_write_read耗时: 22.218310356140137
        # 多进程耗时
        multiprocessing_time(5000000, calculation)  # 10个进程CPU执行1亿次calculation耗时: 1.7116732597351074
        multiprocessing_time(5000000, io_write_read)  # 10个进程CPU执行1亿次io_write_read耗时: 6.813052177429199
    
    
    if __name__ == "__main__":
        main()
    """执行结果
        单线程CPU执行1亿次calculation耗时: 5.335744142532349
        单线程CPU执行1亿次io_write_read耗时: 21.98353886604309
        10个线程CPU执行1亿次calculation耗时: 5.344666814804077
        10个线程CPU执行1亿次io_write_read耗时: 22.28637194633484
        10个进程CPU执行1亿次calculation耗时: 1.8069920539855957
        10个进程CPU执行1亿次io_write_read耗时: 7.141486167907715
    """

    全局解释器锁-解决方法

            1.不使用多线程,使用多进程
            2.不使用C C++做的解释器,用 C# Java
            3.Python多线程适合高用时的网络IO操作,不适用CPU密集型程序
            4.使用其它语言实现多线程,Python去调用

    GIL解决方案示例-C语言写子线程执行的函数由Python调用

            loop.c文件代码

    #include<stdio.h>
    
    void DeadLoop()
    {
        while(1)
        {
            ;
        }
    }
    
    int main(int argc, char **argv)
    {
        printf("start DeadLoop
    ");
        DeadLoop();
        return 0;
    }

            命令行下编译C语言写的代码
                gcc loop.c  # 编译成功后当前目录先会生成一个a.out的可执行文件
               gcc loop.c -shared -o libdead_loop.so  # 执行命令把c语言文件编译成一个动态库文件
               ./a.out  # 执行文件

            Python调用编译后的C语言的动态库文件

    import ctypes
    import threading
    
    
    def main():
        # 加载动态库
        lib = ctypes.cdll.LoadLibrary("libdead_loop.so")
        # 创建一个子线程,让子线程执行C语言写的死循环函数
        t = threading.Thread(target=lib.DeadLoop)
        t.start()
    
    
    if __name__ == "__main__":
        main()

    9.多线程-udp聊天器

    import socket
    import threading
    
    
    def send_msg(udp_socket):
        """获取键盘数据,并将其发送给对方"""
        while True:
            # 1. 从键盘输入数据
            msg = input("
    请输入要发送的数据:")
            # 2. 输入对方的ip地址
            dest_ip = input("
    请输入对方的ip地址:")
            # 3. 输入对方的port
            dest_port = int(input("
    请输入对方的port:"))
            # 4. 发送数据
            udp_socket.sendto(msg.encode("utf-8"), (dest_ip, dest_port))
    
    
    def recv_msg(udp_socket):
        """接收数据并显示"""
        while True:
            # 1. 接收数据
            recv_msg = udp_socket.recvfrom(1024)
            # 2. 解码
            recv_ip = recv_msg[1]
            recv_msg = recv_msg[0].decode("utf-8")
            # 3. 显示接收到的数据
            print(">>>%s:%s" % (str(recv_ip), recv_msg))
    
    
    def main():
        # 1. 创建套接字
        udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # 2. 绑定本地信息
        udp_socket.bind(("", 7890))
    
        # 3. 创建一个子线程用来接收数据
        t = threading.Thread(target=recv_msg, args=(udp_socket,))
        t.start()
        # 4. 让主线程用来检测键盘数据并且发送
        send_msg(udp_socket)
    
    
    if __name__ == "__main__":
        main()

    10.多线程-简单tcp服务器

    from socket import *
    import os
    import sys
    from threading import Thread
    
    
    def client_handler(c):
        try:
            print("子线程接收%s客户端的请求" % str(c.getpeername()))
            while True:
                data = c.recv(1024)
                if not data:
                    break
                print(data.decode("utf-8"))
                c.send(b"receive your message")
        except (KeyboardInterrupt, SystemError):
            raise
        except Exception as e:
            print(e)
        # 关闭客户端套接字
        c.close()
    
    
    def main():
        # 创建套接字
        s = socket()
        # 端口重用
        s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        # 绑定本地信息
        s.bind(("", 7890))
        # 监听
        s.listen(128)
        print("主线程%d等待客户端的链接" % os.getpid())
    
        while True:
            try:
                c, addr = s.accept()
            except KeyboardInterrupt:
                raise
            except Exception as e:
                print(e)
                continue
            t = Thread(target=client_handler, args=(c,))
            t.setDaemon(True)  # 主线程执行完毕其它线程也会终止
            t.start()
        # 关闭监听套接字
        s.close()
    
    
    if __name__ == "__main__":
        main()

    11.多线程-简单tcp服务器的测试客户端

    import socket
    
    
    def main():
        # 创建数据流套接字
        tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 连接服务器
        server_ip = input("请输入要连接的服务器的ip:")
        serve_port = int(input("请输入要连接的服务器的port:"))
        server_addr = (server_ip, serve_port)
        tcp_client_socket.connect(server_addr)
        # 发送数据
        send_data = input("请输入要发生的数据:")
        tcp_client_socket.send(send_data.encode("utf-8"))
        # 接收服务器发送过来的数据
        recv_data = tcp_client_socket.recv(1024)
        print("接收到的数据为:%s" % recv_data.decode("utf-8"))
        # 关闭套接字
        tcp_client_socket.close()
    
    
    if __name__ == "__main__":
        main()
  • 相关阅读:
    webbrowser获取页面文章指定段落内容
    webbrowser防止弹窗(IE)
    webbrowser模拟手动输入
    WPF加载Winform窗体时 报错:子控件不能为顶级窗体
    FAQs: 我们可以在那里来为我的没有提升管理权限的应用程序存储用户数据?
    Winform中修改WebBrowser控件User-Agent的方法(已经测试成功)
    必应代码搜索 Bing Code Search 安装
    Microsoft Visual Studio Professional 2012 专业版 下载
    vs2012 aspx 没有设计视图了?
    vs2010 Express 下载连接
  • 原文地址:https://www.cnblogs.com/tangxuecheng/p/13620828.html
Copyright © 2011-2022 走看看