• 并发编程 线程


    1.线程的概念:

      1. 什么是线程: 进程和线程都是虚拟单位,都是用来帮助我木门形象的描述某种事物

        进程 : 资源单位    线程:执行单位

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

      2. 为什么要有线程:

        开进程 1 申请内存空间 耗资源,  拷贝代码, 耗资源   

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

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

    2.Threading.Thread 创建线程模块

    Thread实例对象的方法
      # isAlive(): 返回线程是否活动的。
      # getName(): 返回线程名。
      # setName(): 设置线程名。
    
    threading模块提供的一些方法:
      # threading.currentThread(): 返回当前的线程变量。
      # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
      # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

    3.代码的实现

      1.创建线程的两种方式

    from threading import Thread
    import time
    
    def func(name):
        print(f'{name}线程正在创建')
        time.sleep(2)
        print(f'{name}线程结束')
    # 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
    if __name__ == '__main__':
        t = Thread(target=func,args=('小明',))
        t.start()
        print('')
    from threading import Thread
    import time
    class MyThread(Thread):
        def __init__(self,name):
            super().__init__()
            self.name = name
    
        def run(self):
            print(f'{self.name}线程正在创建')
            time.sleep(2)
            print(f'{self.name}线程结束')
    
    
    if __name__ == '__main__':
        t = MyThread('小明')
        t.start()
        print('')

      2.线程对象及其他方法:

    from threading import Thread,current_thread,active_count
    import time
    import os
    
    def task(name,i):
        print('%s is running'%name)
        # print('子current_thread:',current_thread().name)
        # print('子',os.getpid())
        time.sleep(i)
    
        print('%s is over'%name)
    # 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
    t = Thread(target=task,args=('egon',1))
    t1 = Thread(target=task,args=('jason',2))
    t.start()  # 告诉操作系统开辟一个线程  线程的开销远远小于进程
    t1.start()  # 告诉操作系统开辟一个线
    t1.join()  # 主线程等待子线程运行完毕
    print('当前正在活跃的线程数',active_count())
    # 小的代码执行完 线程就已经开启了
    print('')
    # print('主current_thread:',current_thread().name)  # 线程名字
    # print('主',os.getpid())

       3. 守护线程:与进程方法相同,但需要注意的是:主线程的结束也就以为着进程的结束,主线程必须等待其他非守护线程的结束才能结束,也就是意味着子线程在运行的时候需要使用进程中的资源,而主线程一旦结束了资源也就销毁了

    from threading import Thread,current_thread
    import time
    
    def task(i):
        print(current_thread().name)
        time.sleep(i)
        print('GG')
    t = Thread(target=task,args=(1,))
    t.daemon = True
    t.start()
    print('')   
    '''
    Thread-1
    主     因为t线程为守护进程,主线程运行结束t线程也跟着结束
    '''

      4.  同一进程中线程之间的数据是共享的

    from threading import Thread
    
    money = 666
    def task():
        global money
        money = 999
    
    t = Thread(target=task)
    t.start()
    t.join()
    print(money)   # 999 同一进程中线程间的数据是共享的

      5.  线程中的互斥锁与进程中的使用方法一致

    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,))  # 创建100个线程
        t.start()  
        t_list.append(t)  # 将线程对象添加到列表中
    for t in t_list:  # 遍历线程对象列表
        t.join()   # 等待所有线程程运行结束再执行主线程
    print(n)   # 0 

      6. 用线程实现sockte服务端的并发:

        服务端:

    import socket
    from threading import Thread
    def talk(conn):
        while 1:
            try:
                ret = conn.recv(1024)
                if len(ret) == 0: break
                print(ret)
                conn.send(ret.upper())
            except ConnectionResetError:
                break
        conn.close()
    
    
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    
    while 1:
        conn,addr = sk.accept()
        t = Thread(target=talk,args=(conn,))
        t.start()

        客户端:

    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    while 1:
        sk.send(b'hello')
        ret = sk.recv(1024)
        print(ret)

      7: GIL全局解释器锁:

      

    """
    In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple
    native threads from executing Python bytecodes at once. This lock is necessary mainly
    because CPython’s memory management is not thread-safe.
    """

    ps:python解释器有很多种 最常见的就是Cpython解释器
      GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全 他是用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)GIL的存在是因为CPython解释器的内存管理不是线程安全的,也就是说同一个进程中的多个线程再同一时间只有一个线程被cpu执行,GIL全局解释器锁只是针对线程的,进入IO自动释放

      总结.在执行多个计算密集型的任务时.在计算机单核的情况下开线程最好,在多核情况下开进程会效率更高

        .在执行多个IO密集型的任务时.开线程时最好的

      8:死锁与递归锁:当一个进程或线程中同时出现多把锁容易造成死锁显现,程序卡死

       递归锁: Rlock,可以多次acquire与release  当一个线程或进程中所有acquire都被release,其他的线程才能获得资源,注意使用递归锁时是链式赋值

        科学家吃面示例:

    import time
    from threading import Thread,RLock
    lock1 = lock2 = RLock()
    def eat1(name):
        lock2.acquire()
        print('%s 抢到了面条'%name)
        lock1.acquire()
        print('%s 抢到了叉子'%name)
        print('%s 吃面'%name)
        lock1.release()
        lock2.release()
    
    def eat2(name):
        lock1.acquire()
        print('%s 抢到了叉子' % name)
        time.sleep(1)
        lock2.acquire()
        print('%s 抢到了面条' % name)
        print('%s 吃面' % name)
        lock2.release()
        lock1.release()
    
    for name in ['小明','小黄','小亮']:
        t1 = Thread(target=eat1,args=(name,))
        t2 = Thread(target=eat2,args=(name,))
        t1.start()
        t2.start()

      

      9 信号量: Semaphore: 用锁的原理实现的,内置了一个计数器,在同一时间只能有指定数量的进程或线程执行某一段代码,如果把互斥锁比作一个车位的话,那么信号量相当于多个车位

      

    from threading import Thread,Semaphore  # 线程使用信号量导入方法
    # from multiprocessing import Process,Semaphore  # 进程使用信号量导入方法
    import time
    import random
    
    sm = Semaphore(5)  # 5个车位
    
    def func(name):
        sm.acquire()  # 占车位
        print(f'{name}占了一个车位')
        time.sleep(random.randint(1,2))
        sm.release()  # 让出车位
        print(f'{name}让了一个车位')
    
    for i in range(20):
        t = Thread(target=func,args=(i,))
        t.start()
    '''同时占用车位最多不会超过5个,一个让出去另一个才会占用
    '''

      10:事件:event类似于join 不过事件是在处理两个子进程或子线程之间互相等待的过程

        常用方法:

    from multiprocessing import Event
    e = Event()
    e.wait()  # 阻塞态,状态为True不堵塞,为False 堵塞
    e.is_set()  # 查看状态 
    e.set()  # False  ---> True
    e.clear()  # True ---> False

        红绿灯示例:

    from threading import Event,Thread
    import time
    e = Event()
    
    def light():
        print('红灯亮了')
        time.sleep(3)
        e.set()
        print('绿灯亮了')
    
    def car(name):
        print(f'{name}正在等红灯')
        e.wait()
        print(f'{name}可以行驶了')
    
    l = Thread(target=light)
    l.start()
    for i in range(10):
        c = Thread(target=car,args=(i,))
        c.start()

      11. 线程队列: 

        1: queue  队列 

    import queue
    q = queue.Queue()
    q.put(1)
    q.put(2)
    print(q.get())  # 1
    print(q.get())  # 2

        2: Lifoqueue 堆栈

    import queue
    q = queue.LifoQueue()  # 堆栈
    q.put(1)
    q.put(2)
    print(q.get())  # 2
    print(q.get())  # 1

        3: PriorityQueue  优先级队列

    import queue
    q = queue.PriorityQueue() # 堆栈
    q.put((1,'a'))  # 放一个元组,第一个元素是优先级为int型,数字越小优先级越高
    q.put((0,'b'))  
    q.put((-1,'c'))
    q.put((1,'d'))  # 同一优先级,按照内容对应的ASCII码表来确认优先级
    print(q.get())  # (-1, 'c')
    print(q.get()) # (0, 'b')
    print(q.get()) # (1, 'a')
    print(q.get())  # (1, 'd')
  • 相关阅读:
    数据源ObjectDataSource的数据访问类的编写
    ASP.NET网页文本编辑器的使用
    装饰模式
    策略模式
    代理模式
    备份、还原数据库
    简单工厂和工厂模式
    ASP.NET上传多个文件
    数据库访问类的编写
    UVA 11069 A Graph Problem
  • 原文地址:https://www.cnblogs.com/yanglingyao/p/11342136.html
Copyright © 2020-2023  润新知