python 线程 进程与线程 GIL: 计算密集型与I/O密集型 线程锁 信号量锁: enevt同步

一颗cpu同一时刻只能有一个线程执行。在cpython解释器中,同一时刻只能让同一个进程中只有一个线程进入解释器

GIL是内核级别的锁:为了保护内核,被多线程干扰

线程锁是用户级别的锁:为了保护程序里面的数据

  线程:

    是最小的执行单位,是一堆指令的集合。cpu会记录上下文关系

    数据之间是共享的

    一个进程至少有一个线程

  进程:

    进程是不修改数据的。

    一堆资源(线程、cpu、内存等)的管理的集合

    进程之间的数据是不共享的。

GIL:

  在cpython解释器中,同一时刻只能让同一个进程中只有一个线程进入解释器。因为同一进程中的线程是共享数据的

  在开发这么语言的时候,只有一颗cpu,为了保护在多线程保护数据一致性,所以开发了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. (However, since the GIL exists,

  other features have grown to depend on the guarantees that it enforces.)

计算密集型与I/O密集型

io密集型:

  线程在遇到I/0(sock的收发,文件的读写,sleep等)的时候,会自动进行切换到另外一个线程计算。就算只有一颗cpu的情况下,也大量节省了时间。

  如下代码,多线程会比串行节约一倍的时间

import time
import threading
def foo1():
    time.sleep(8)
    print("1")
def foo2():
    time.sleep(8)
    print("2")

start=time.time()
# foo1()
# foo2()
a=threading.Thread(target=foo1)
b=threading.Thread(target=foo2)
a.start()
b.start()
a.join()
b.join()
end=time.time()
print("run time:",end-start)
View Code

计算密集型:

   下列代码,就是io密集型的表现,使用串行算法与 多线程算法 两者的时间几乎没区别(在python2.7和3.4中,多线程io因为不断切换线程,时间比串行慢了一半。3.5已经优化) python中因为GIL的存在,线程只能在一个cpu中计算。对应计算密集型多线程 不适用

import time
import threading
def add(n):
    sum = 0
    for i in range(n):
        sum += i
    print(sum)
start = time.time()
# add(100000000)
# add(200000000)
a=threading.Thread(target=add,args=(100000000,))
b=threading.Thread(target=add,args=(200000000,))
a.start()
b.start()
a.join()
b.join()
end = time.time()

print("time:",start-end)
View Code

  解决同一个进程中的多线程在同一时刻只能有一个进入cpu的问题,可以利用多协程与携程 等等解决方法。

线程的另外一种调用方式:

import threading
import time
 
 
class MyThread(threading.Thread):
    def __init__(self,num):
        threading.Thread.__init__(self)
        self.num = num
 
    def run(self):#定义每个线程要运行的函数
 
        print("running on number:%s" %self.num)
 
        time.sleep(3)
 
if __name__ == '__main__':
 
    t1 = MyThread(1)
    t2 = MyThread(2)
    t1.start()
    t2.start()
View Code

jion方法:

  阻塞线程,等待线程执行完毕。  线程可以通过列表装载。

setdeamon:

  守护进程,主进程死了,下面的线程全挂、

线程锁

是为了解决线程之间 操作共享数据的问题

线程不安全:

  如下代码:线程之间共享数据,假如第一次一个线程拿到全局变量,进行计算的时候,到了cpu的执行时间,不会进行最后一步重新赋值全局变量。

  但是第二次线程拿到全局变量,执行完毕,赋值给全局变量。两次的执行效果都是一样。

import threading,time
def add():
    global num
    temp = num
    temp -= 1
    time.sleep(0.001)
    num = temp
num = 200
thread_list=[]
for i in range(100):
    t = threading.Thread(target=add,args=())
    t.start()
    thread_list.append(t)
for t in thread_list:
    t.join()
print(num)
View Code

      解决方案:线程锁,就是加在线程上,告诉解释器,我必须执行完,你才能给我进行切换!

 acquire 与 release 之间变成了串行,就算遇到I/0也会被锁住

但是在该线程 锁之外的东西(如下print(ok))还是可以进行cpu切换的,
这就是和join的区别。 join是把线程所有的部分都变成了串行
import threading,time
def add():
    global num
    #!!acquire 与 release 之间变成了串行,就算遇到I/0也会被锁住
    # 但是在该线程 锁之外的东西(如下print(ok))还是可以进行cpu切换的,
    # 这就是和join的区别。 join是把线程所有的部分都变成了串行
    print("ok")
    r.acquire()
    temp = num
    temp -= 1
    time.sleep(0.001)
    num = temp
    r.release()
num = 200
thread_list=[]
r = threading.Lock()
for i in range(100):
    t = threading.Thread(target=add,args=())
    t.start()
    thread_list.append(t)
for t in thread_list:
    t.join()
print(num)
View Code

GIL 是 一个进程的所有线程,同一时刻只能一个进入cpu

线程锁 是保证 一个进程中,多线程操作共享数据的时候,为了避免到时 切换cpu而操作数据的不安全。

  递归锁:普通的锁 只能被使用一次, 相互等待锁的时候,就会被锁死。(如下代码)递归锁 维护了 一个 计数器与对应列表。

import threading,time

class myThread(threading.Thread):
    def doA(self):
        lockA.acquire()
        print(self.name,"gotlockA",time.ctime())
        time.sleep(3)
        lockB.acquire()
        print(self.name,"gotlockB",time.ctime())
        lockB.release()
        lockA.release()

    def doB(self):
        lockB.acquire()
        print(self.name,"gotlockB",time.ctime())
        time.sleep(2)
        lockA.acquire()
        print(self.name,"gotlockA",time.ctime())
        lockA.release()
        lockB.release()
    def run(self):
        self.doA()
        self.doB()
if __name__=="__main__":

    lockA=threading.Lock()
    lockB=threading.Lock()
    threads=[]
    for i in range(5):
        threads.append(myThread())
    for t in threads:
        t.start()
    for t in threads:
        t.join()#等待线程结束,后面再讲。
View Code

   递归锁在类中包含 账户 转账、取现的时候,每个方法都加锁,同时一个方法调用前面函数,就需要用到递归锁了

信号量锁:

同一时刻只有三个链接并发。

r = threading.BoundedSemaphore(3)
r.acqure()
r.release()

条件同步锁

     有一类线程需要满足条件之后才能够继续执行,Python提供了threading.Condition 对象用于条件变量线程的支持,

    它除了能提供RLock()或Lock()的方法外,还提供了 wait()、notify()、notifyAll()方法。

      lock_con=threading.Condition([Lock/Rlock]): 锁是可选选项,不传人锁,对象自动创建一个RLock()。

wait():条件不满足时调用,线程会释放锁并进入等待阻塞;等到了notify通知时候,会从acquire从新执行
notify():条件创造后调用,通知等待池激活一个线程;
notifyAll():条件创造后调用,通知等待池激活所有线程。

enevt同步

  这个不是锁了,但是效果类似条件同步锁

  创建的时候,默认是Flase        flag=thread.Event()

event.isSet():返回event的状态值;

event.wait():如果 event.isSet()==False将阻塞线程;当set的时候,立马执行

event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;

event.clear():恢复event的状态值为False。
import threading,time
import random
def light():
    if not event.isSet():
        event.set() #wait就不阻塞 #绿灯状态
    count = 0
    while True:
        if count < 10:
            print('