• 线程


    线程

    线程基础

    什么是线程?

    进程线程其实都是虚拟单位,都是用来帮助我们形象的描述某种事物。

    进程:是一个资源单位

    线程:执行单位

    我们可以将内存比如工厂,那么进程就相当于是工厂里面的车间,而线程就相当于是车间里面的流水线。

    ps:每个进程都自带一个线程,线程才是真正的执行单位,进程只是在线程运行过程中,提供代码运行所需要的资源。

    为什么要有线程?

    因为开线程需要申请内存空间,很耗资源,也需要"拷贝代码",也很耗资源。

    而线程,一个进程内可以起多个线程,并且线程与线程之间数据是可以共享的。

    ps:开启线程的开销要远远小于开启进程的开销

    创建线程的两种方式

    第一种:通过Thread模块直接创建

    from threading import Thread
    import time
    
    
    def task(name):
        print(f"{name} is running")
        time.sleep(3)
        print(f'{name} is over')
    
    # 开线程不需要在__main__代码块内,但是习惯性的还是写在__main__代码块内
    t = Thread(target=task,args=("egon",))
    t.start()  # 告诉操作系统开辟一个线程 线程的开销远远小于进程  小的代码执行完 线程就已经开启了
    print("主")
    
    egon is running主
    
    egon is over
    

    第二种:通过自定义类来实现

    from threading import Thread
    import time
    
    class MyThread(Thread):
        def __init__(self,name):
            super(MyThread, self).__init__()
            self.name = name
    
        def run(self):
            print(f"{self.name} is running")
            time.sleep(3)
            print(f"{self.name} is over")
    
    t = MyThread('egon')
    t.start()
    print("主")
    
    egon is running主
    
    egon is over
    

    线程对象及其他方法

    pid 获取进程号

    from threading import Thread
    import time
    import os
    
    def task(name,i):
        print(f"{name} is running")
        print("子",os.getpid())
        time.sleep(i)
    
        print(f'{name} is over')  
    
    
    t = Thread(target=task,args=("egon",1))
    t1 = Thread(target=task,args=("jason",2))
    t.start()
    t1.start()
    
    print("主")
    print("主",os.getpid())
    
    egon is running
    子 3348
    jason is running主
    主 3348
    子 3348
    
    egon is over
    jason is over
    

    current_thread().name 当前线程变量的名字

    from threading import Thread,current_thread
    import time
    import os
    
    def task(name,i):
        print(f"{name} is running")
        print("子current_thread:",current_thread().name)
    
        time.sleep(i)
    
        print(f'{name} is over')  
    
    
    t = Thread(target=task,args=("egon",1))
    t1 = Thread(target=task,args=("jason",2))
    t.start()
    t1.start()
    
    
    print("主")
    print("主current_thread:",current_thread().name)
    
    egon is running
    子current_thread: Thread-1
    jason is running主
    
    主current_thread: MainThread
    子current_thread: Thread-2
    egon is over
    jason is over
    

    active_count() 正在运行的线程数量

    from threading import Thread,active_count
    import time
    import os
    
    def task(name,i):
        print(f"{name} is running")
        time.sleep(i)
    
        print(f'{name} is over')  #
    
    t = Thread(target=task,args=("egon",1))
    t1 = Thread(target=task,args=("jason",2))
    t.start()  # 告诉操作系统开辟一个线程  线程的开销远远小于进程
    t1.start() # 告诉操作系统开辟一个线程  线程的开销远远小于进程
    
    
    t1.join()  # 主线程等待子线程运行我完毕后执行
    print("当前正在活跃的线程数",active_count())
    
    print("主")
    
    egon is running
    jason is running
    egon is over
    jason is over
    当前正在活跃的线程数 1
    主
    

    内存共享问题

    多个线程共享一个进程里面的数据。如果其中一个线程对进程的里面的数据进行了改变,则这个数据就发生了变化。

    from threading import Thread
    
    money = 666
    
    def task():
        global money
        money = 999
    
    
    t = Thread(target=task)
    t.start()
    t.join()
    print(money)  # 999
    

    守护线程

    无论是进程还是线程,都遵循:守护xx会等待主xx运行完毕后被销毁。需要强调的是:运行完毕并非终止运行。

    1.对主进程来说,运行完毕指的是主进程代码运行完毕

    2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕。

    详细解释

    1.主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束。

    2.主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都被回收,而进程必须保证非守护线程都运行完毕后才能结束。

    守护线程例子1

    from threading import Thread,current_thread
    import time
    
    def task(i):
        print(current_thread().name)
        time.sleep(i)
        print("GV")
    
    for i in range(3):
        t = Thread(target=task,args=(i,))
        t.start()
    
    
    t = Thread(target=task,args=(1,))
    t.daemon = True
    t.start()
    print("主")
    print(t.is_alive())
    """
    Thread-1
    GV
    Thread-2
    Thread-3
    Thread-4
    主  # 执行到这里,主进程必须等待非守护进程执行完毕才能结束
    True
    GV
    GV
    GV
    
    """
    

    守护线程例子2

    from threading import Thread
    from multiprocessing import Process
    import time
    
    def foo():
        print(123)
        time.sleep(1)
        print("end123")
    
    
    def bar():
        print(456)
        time.sleep(3)
        print("end456")
    
    
    if __name__ == '__main__':
        t1 = Thread(target=foo)
        t2 = Thread(target=bar)
        t1.daemon=True
        t1.start()
        t2.start()
        print("main------")
    """
    123
    456main------
    
    end123
    end456
    """    
    

    同步锁

    多个资源抢占资源的情况

    from threading import Thread
    import os,time
    def task():
        global n
        temp = n
        time.sleep(0.1)
        n = temp - 1
    
    if __name__ == '__main__':
        n = 100
        l = []
        for i in range(100):
            p = Thread(target=task)
            l.append(p)
            p.start()
        for p in l:
            p.join() # 主线程等待子线程运行我完毕后执行
    
        print(n)  # 99
    
    

    对公共数据的操作

    import threading
    R = threading.Lock()
    R.acquire()
    """
    对公共数据的操作
    """
    R.release()
    
    

    同步锁的引用

    将线程由原来的并发执行变成了串行,牺牲了执行效率保证了数据安全。

    from threading import Thread,Lock
    import time
    
    n = 100
    def task(mutex):
        global n
        # 加锁的代码变成串行运行
        mutex.acquire()
        tmp = n
        time.sleep(0.1)
        n = tmp - 1
        mutex.release()
    
    
    t_list = []
    mutex = Lock()
    for i in range(100):
        t = Thread(target=task,args=(mutex,))
        t.start()
        t_list.append(t)
    
    for t in t_list:
        t.join() # 主线程等待子线程运行我完毕后执行
    print(n)  # 0
    
    

    互斥锁和join的区别

    #不加锁:并发执行,速度快,数据不安全
    from threading import current_thread,Thread,Lock
    import os,time
    def task():
        global n
        print('%s is running' %current_thread().getName())
        temp=n
        time.sleep(0.5)
        n=temp-1
    
    
    if __name__ == '__main__':
        n=100
        lock=Lock()
        threads=[]
        start_time=time.time()
        for i in range(100):
            t=Thread(target=task)
            threads.append(t)
            t.start()
        for t in threads:
            t.join()
    
        stop_time=time.time()
        print('主:%s n:%s' %(stop_time-start_time,n))
    
    '''
    Thread-1 is running
    Thread-2 is running
    ......
    Thread-100 is running
    主:0.5216062068939209 n:99
    '''
    
    
    #不加锁:未加锁部分并发执行,加锁部分串行执行,速度慢,数据安全
    from threading import current_thread,Thread,Lock
    import os,time
    def task():
        #未加锁的代码并发运行
        time.sleep(3)
        print('%s start to run' %current_thread().getName())
        global n
        #加锁的代码串行运行
        lock.acquire()
        temp=n
        time.sleep(0.5)
        n=temp-1
        lock.release()
    
    if __name__ == '__main__':
        n=100
        lock=Lock()
        threads=[]
        start_time=time.time()
        for i in range(100):
            t=Thread(target=task)
            threads.append(t)
            t.start()
        for t in threads:
            t.join()
        stop_time=time.time()
        print('主:%s n:%s' %(stop_time-start_time,n))
    
    '''
    Thread-1 is running
    Thread-2 is running
    ......
    Thread-100 is running
    主:53.294203758239746 n:0
    '''
    
    # 有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊
    
    # 没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是
    
    # start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的
    
    # 单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高.
    from threading import current_thread,Thread,Lock
    import os,time
    def task():
        time.sleep(3)
        print('%s start to run' %current_thread().getName())
        global n
        temp=n
        time.sleep(0.5)
        n=temp-1
    
    
    if __name__ == '__main__':
        n=100
        lock=Lock()
        start_time=time.time()
        for i in range(100):
            t=Thread(target=task)
            t.start()
            t.join()
        stop_time=time.time()
        print('主:%s n:%s' %(stop_time-start_time,n))
    
    '''
    Thread-1 start to run
    Thread-2 start to run
    ......
    Thread-100 start to run
    主:350.6937336921692 n:0 #耗时是多么的恐怖
    '''
    
    )
    
    
    

    Thread类的其他方法

    Thread实例对象的方法:

    • isAlive():返回线程是否活动的。
    • getName():返回线程名。
    • setName():设置线程名。

    threading模块提供的一些方法:

    • threading.currentThread():返回当前的线程变量。
    • threading.enumerate():返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
    • threading.activeCount():返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

    代码示例

    from threading import Thread
    import threading
    from multiprocessing import Process
    import os
    
    def work():
        import time
        time.sleep(3)
        print(threading.current_thread().getName())
    
    
    if __name__ == '__main__':
        # 在主进程下开启线程
        t=Thread(target=work)
        t.start()
    
        print(threading.current_thread().getName())
        print(threading.current_thread()) # 主线程
        print(threading.enumerate()) # 连同主线程在内有两个运行的线程
        print(threading.active_count())
        print('主线程/主进程')
    
        '''
        打印结果:
        MainThread
        <_MainThread(MainThread, started 140735268892672)>
        [<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>]
        主线程/主进程
        Thread-1
        '''
    
    

    join方法

    from threading import Thread
    import time
    def sayhi(name):
        time.sleep(2)
        print('%s say hello' %name)
    
    if __name__ == '__main__':
        t=Thread(target=sayhi,args=('nick',))
        t.start()
        t.join()
        print('主线程')
        print(t.is_alive())
        '''
        nick say hello
        主线程
        False
        '''
    
    

    多线程实现socket

    服务端

    import multiprocessing
    import threading
    
    import socket
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.bind(('127.0.0.1',8080))
    s.listen(5)
    
    def action(conn):
        while True:
            data=conn.recv(1024)
            print(data)
            conn.send(data.upper())
    
    if __name__ == '__main__':
    
        while True:
            conn,addr=s.accept()
    
    
            p=threading.Thread(target=action,args=(conn,))
            p.start()
    
    

    客户端

    import socket
    
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect(('127.0.0.1',8080))
    
    while True:
        msg=input('>>: ').strip()
        if not msg:continue
    
        s.send(msg.encode('utf-8'))
        data=s.recv(1024)
        print(data
    
    
  • 相关阅读:
    golang入门--一个简单的http client
    Linux 安装JDK1.8
    spring boot配置拦截器和过滤器
    spring boot swagger配置
    spring boot语言国际化
    element-ui 中为表头添加tooltips
    spring boot定时任务的使用
    Windows编译运行webrtc全过程,并实现屏幕共享
    WLYX官方团队の规则
    AVL树的平衡算法(JAVA实现)
  • 原文地址:https://www.cnblogs.com/zuihoudebieli/p/11341855.html
Copyright © 2020-2023  润新知