一,线程Thread模块
1.效率更高(相对于进程)



import time
    from multiprocessing import Process
    from threading import Thread
    def func(a):
        a=a+1

    if __name__=="__main__":
        start=time.time()
        t_l=[]
        for i in range(50):
            t=Thread(target=func,args=(i,))
            t_l.append(t)
            t.start()
        for t in t_l:
            t.join()
        print("主线程结束")
        print(time.time()-start)       # 0.010993003845214844


        start=time.time()
        p_l=[]
        for i in range(50):
            p=Process(target=func,args=(i,))
            p_l.append(p)
            p.start()
        for p in p_l:
             p.join()
        print("主进程")
        print(time.time()-start)       # 6.235144376754761



2.线程之间的数据共享 (进程中数据是隔离的):



from threading import Thread
    n=100
    def func():
        global n       #子线程引用主线程的变量       (进程中不可以引用)
        n=n-1           #并修改主线程的变量值

    t_l=[]
    for i  in range(100):
        t=Thread(target=func)
        t_l.append(t)
        t.start()
    for t in t_l:
        t.join()
    print(n)       # 0



3.守护线程



import time
    from threading import Thread
    def func1():
        while True:
            print("True")      #4.打印True  11.**   13.**
            time.sleep(0.5)      # 5.睡1 秒   #12.又休1秒   14.又休1秒


    def func2():
        print("in f2 start")     #8打印in f2 start
        time.sleep(3)              #9,休3秒
        print("in f2 end")       #15打印   in f2  end


    t1=Thread(target=func1)    #1.创建一个线程
    t1.setDaemon(True)         #2.设置成守护进程
    t1.start()                 #3.申请开启线程
    t2=Thread(target=func2)     #6.创建另一个线程
    t2.start()                  #7.申请开启另一个线程
    time.sleep(1)               # 12.**
    print('主线程')            #10.  打印主线程总结:主线程如果结束了,那么整个进程就结束,所有线程就结束



守护进程:只守护主进程的代码就可以了,守护进程会在主进程的代码执行完毕之后直接结束,无论守护进程是否执行完毕

4.开启线程的第二种方式:



import time
    from threading import Thread,get_ident
    class MyThread(Thread):
        def __init__(self,args):
            super().__init__()
            self.args=args

        def run(self):
            print("in my thread:" ,get_ident(), self.args)
    print("in main",get_ident())      # in main 11676
    m=MyThread("hahaha")
    m.start()                          #  in my thread: 22100 hahaha



5.Thread类的其他方法
Thread实例对象的方法:
isAlive():返回线程是否执行
getName():返回线程名
setName():设置线程名

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



import time
    import threading
    from threading import Thread,get_ident,currentThread
    print(threading.currentThread().getName())     #  MainThrea
    print(threading.enumerate())    #[<_MainThread(MainThread, started 9780)>]
    print(threading.activeCount())   #1



二.线程中的锁 Lock



1. 什么时候用锁:
    在多个进程\线程同时访问一个数据的时候就会产生数据的不安全的现象
2.区别GIL 全局解释器锁
    在同一个进程里的每一个线程同一时间只能有一个线程访问CPU
3.尽量不要设置全局变量
    只要在多线程/进程之间用到全局变量,就加上锁
第一版:



import time
    import threading
    from threading import Thread,get_ident,currentThread
    print(threading.currentThread().getName())     #  MainThrea
    print(threading.enumerate())    #[<_MainThread(MainThread, started 9780)>]
    print(threading.activeCount())   #1



第二版:科学家吃面:
1.死锁现象:
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,
它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程



import time
    from threading import Thread,Lock
    lock=Lock()
    noodle_lock=Lock()
    fork_lock=Lock()
    def eat1(name):
        noodle_lock.acquire()
        print("%s拿到了面" % name)
        fork_lock.acquire()
        print("%s拿到了叉子"%name)
        print("%s在吃面"%name)
        time.sleep(0.5)
        fork_lock.release()
        noodle_lock.release()


    def eat2(name):
        fork_lock.acquire()
        print("%s拿到了叉子"%name)
        noodle_lock.acquire()
        print("%s拿到面条"%name)
        print("%s在吃面"%name)
        time.sleep(0.5)
        noodle_lock.release()
        fork_lock.release()
    eat_l=["alex","wusir","太白","苑"]
    for name in eat_l:
        Thread(target=eat1,args=(name,)).start()
        Thread(target=eat2,args=(name,)).start()



2.递归锁(Rlock):
递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一
个线程所有的acquire都被release,其他的线程才能获得资源
3.用递归锁解决上述死锁问题:



import time
    from threading import Thread,RLock
    lock=RLock()

    def eat1(name):
        lock.acquire()
        print("%s拿到了面" % name)
        lock.acquire()
        print("%s拿到了叉子"%name)
        print("%s在吃面"%name)
        time.sleep(0.5)
        lock.release()
        lock.release()


    def eat2(name):
        lock.acquire()
        print("%s拿到了叉子"%name)
        lock.acquire()
        print("%s拿到面条"%name)
        print("%s在吃面"%name)
        time.sleep(0.5)
        lock.release()
        lock.release()
    eat_l=["alex","wusir","太白","苑"]
    for name in eat_l:
        Thread(target=eat1,args=(name,)).start()
        Thread(target=eat2,args=(name,)).start()



4.互斥锁解决死锁问题



import time
    from threading import Thread,Lock
    lock=Lock()

    def eat1(name):
        lock.acquire()
        print("%s拿到了面" % name)
        print("%s拿到了叉子"%name)
        print("%s在吃面"%name)
        time.sleep(0.5)
        lock.release()


    def eat2(name):
        lock.acquire()
        print("%s拿到了叉子"%name)
        print("%s拿到面条"%name)
        print("%s在吃面"%name)
        time.sleep(0.5)
        lock.release()

    eat_l=["alex","wusir","太白","苑"]
    for name in eat_l:
        Thread(target=eat1,args=(name,)).start()
        Thread(target=eat2,args=(name,)).start()



面试题:
    互斥锁和递归锁哪个好:
        递归锁     快速恢复服务
        死锁问题的出现是程序的设计或者逻辑的问题
        还应该进一步的排除和重构逻辑来保证使用互斥锁也不会发生死锁
    互斥锁与递归锁的区别:
        互斥锁:就是在一个线程中不能连续多次acquire
        递归锁:可以在同一个线程中acquire任意次,注意acquire多少次就需要release多少次
三.线程中的信号量 Semaphore
信号量: 锁 + 计数器
信号量  VS  进程池



import time
    from multiprocessing import Semaphore,Process,Pool
    def ktv1(sem,i):
        sem.acquire()
        i+=1
        sem.release()
    def ktv2(i):
        i+=1
   if __name__=="__main__":
    #信号量
        sem=Semaphore(5)
        start=time.time()
        p_l=[]
        for i in range(100):
            p=Process(target=ktv1,args=(sem,i))
            p.start()
            p_l.append(p)
        for p in p_l:
            p.join()
        print("###",time.time()-start)            # 7.551328897476196
    #进程池
        start=time.time()
        p = Pool(5)
        for i in range(100):
            ret=p.apply_async(func=ktv2,args=(p,i))
        p.close()
        p.join()
        print("@@@",time.time()-start)        #@@@ 1.013512372970581



Thread信号量:



import time
    from threading import Semaphore,Thread
    def ktv1(sem, i):
        sem.acquire()
        i += 1
        sem.release()
    def ktv2(i):
        i += 1
    if __name__ == "__main__":
        sem = Semaphore(5)
        start = time.time()
        p_l = []
        for i in range(100):
            t = Thread(target=ktv1, args=(sem, i))
            t.start()
            p_l.append(t)
        for t in p_l:
            t.join()
        print("###", time.time() - start)  #  ### 0.01998734474182129



总结:
    进程池
    进程池的效率更高;
    池子里有几个就起几个
    不管多少任务,池子的个数是固定的
    开启进程和关闭进程这些事都是需要固定的开销的
    不产生额外的时间开销
    信号量:
    有多少任务就起多少进程/线程
    可以帮助你减少操作系统切换的负担
    但是并不能帮助你减少进程/线程开启和关闭的时间
四.线程中的事件 Event
事件四个方法:
    wait : 等到事件内部的信号变成True就不阻塞了,
    set: 设置信号为True
    clear : 设置信号变成False
    is_set : 查看信号是否为True
示例:



import time
    import random
    from threading import Thread,Event
    def check(e):
        print("正在检测两台机器之间的网络情况")
        time.sleep(random.randint(0,2))
        e.set()
    
    def connect_db(e):
        n = 0
        while n < 3:
            if e.is_set():
                break
            else:
                e.wait(0.5)
                n += 1
        if n == 3:
            raise TimeoutError
        print('连接数据库 ... ')
        print('连接数据库成功~~~')
    e=Event()
    Thread(target=connect_db,args=(e,)).start()
    Thread(target=check,args=(e,)).start()



五.条件 Condition



condition :
    Condition被称为条件变量,除了提供与Lock类似的acquire和release方法外,还提供了wait和notify方法。线程首先acquire一个
    条件变量,然后判断一些条件。如果条件不满足则wait;如果条件满足,进行一些处理改变条件后,通过notify方法通知其他线程,
    其他处于wait状态的线程接到通知后会重新判断条件。不断的重复这一过程,从而解决复杂的同步问题。
condition 的相关方法
acquire
release
wait  阻塞
notify   让wait解除阻塞的工具
在执行wait和notify这两个方法的前后 必须执行acquire和release



from threading import Condition,Thread
    def func(con,i):
        con.acquire()
        con.wait()
        print("threaing:",i)
        con.release()

    con=Condition()
    for i in range(20):
        Thread(target=func,args=(con,i)).start()
    con.acquire()
    con.notify_all()
    con.release()
    while True:
        num=int(input("num>>>"))
        con.acquire()
        con.notify(num)
        con.release()



六.定时器 Timer



指定n秒后执行某个操作



from threading import Timer
def func():
    print("in func,执行吧")
Timer(1,func).start()



 七.多线程起socket(tcp)

服务器代码



import socket
import os
sk=socket.socket()
sk.connect(("127.0.0.1",9991))
while True:
    print(sk.recv(1024))
    sk.send(str(os.getpid()).encode('utf-8'))
sk.close()



客户端代码



import socket
import os
sk=socket.socket()
sk.connect(("127.0.0.1",9991))
while True:
    print(sk.recv(1024))
    sk.send(str(os.getpid()).encode('utf-8'))
sk.close()