python 线程

一颗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('33[42;1m--green light on---33[0m')
        elif count <13:
            print('33[43;1m--yellow light on---33[0m')
        elif count <20:
            if event.isSet():
                event.clear()
            print('33[41;1m--red light on---33[0m')
        else:
            count = 0
            event.set() #打开绿灯
        time.sleep(1)
        count +=1
def car(n):
    while 1:
        time.sleep(random.randrange(10))
        if  event.isSet(): #绿灯
            print("car [%s] is running.." % n)
        else:
            print("car [%s] is waiting for the red light.." %n)
if __name__ == '__main__':
    event = threading.Event()
    Light = threading.Thread(target=light)
    Light.start()
    for i in range(3):
        t = threading.Thread(target=car,args=(i,))
        t.start()
View Code

队列:今后常用的线程之间通信的利器

     在单线程这个根本没啥用,在多线程中,使用列表 线程不安全(多个线程 取或者放会覆盖)。quqe自己默认有个锁,保证了线程的安全。

创建一个“队列”对象
import Queue
q = Queue.Queue(maxsize = 10)
Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。

将一个值放入队列中
q.put(10)
调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为
1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。

将一个值从队列中取出
q.get()
调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。

Python Queue模块有三种队列及构造函数:
1、Python Queue模块的FIFO队列先进先出。  class queue.Queue(maxsize)
2、LIFO类似于堆,即先进后出。             class queue.LifoQueue(maxsize)
3、还有一种是优先级队列级别越低越先出来。   class queue.PriorityQueue(maxsize)

此包中的常用方法(q = Queue.Queue()):
q.qsize() 返回队列的大小
q.empty() 如果队列为空,返回True,反之False
q.full() 如果队列满了,返回True,反之False
q.full 与 maxsize 大小对应
q.get([block[, timeout]]) 获取队列,timeout等待时间
q.get_nowait() 相当q.get(False)
非阻塞 q.put(item) 写入队列,timeout等待时间
q.put_nowait(item) 相当q.put(item, False)
q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
q.join() 实际上意味着等到队列为空,再执行别的操作

实例1:

import threading,queue
from time import sleep
from random import randint
class Production(threading.Thread):
    def run(self):
        while True:
            r=randint(0,100)
            q.put(r)
            print("生产出来%s号包子"%r)
            sleep(1)
class Proces(threading.Thread):
    def run(self):
        while True:
            re=q.get()
            print("吃掉%s号包子"%re)
if __name__=="__main__":
    q=queue.Queue(10)
    threads=[Production(),Production(),Production(),Proces()]
    for t in threads:
        t.start()
View Code

实例2:

import time,random
import queue,threading
q = queue.Queue()
def Producer(name):
  count = 0
  while count <20:
    time.sleep(random.randrange(3))
    q.put(count)
    print('Producer %s has produced %s baozi..' %(name, count))
    count +=1
def Consumer(name):
  count = 0
  while count <20:
    time.sleep(random.randrange(4))
    if not q.empty():
        data = q.get()
        print(data)
        print('33[32;1mConsumer %s has eat %s baozi...33[0m' %(name, data))
    else:
        print("-----no baozi anymore----")
    count +=1
p1 = threading.Thread(target=Producer, args=('A',))
c1 = threading.Thread(target=Consumer, args=('B',))
p1.start()
c1.start()
View Code

实例3:

#实现一个线程不断生成一个随机数到一个队列中(考虑使用Queue这个模块)
# 实现一个线程从上面的队列里面不断的取出奇数
# 实现另外一个线程从上面的队列里面不断取出偶数

import random,threading,time
from queue import Queue
#Producer thread
class Producer(threading.Thread):
  def __init__(self, t_name, queue):
    threading.Thread.__init__(self,name=t_name)
    self.data=queue
  def run(self):
    for i in range(10):  #随机产生10个数字 ,可以修改为任意大小
      randomnum=random.randint(1,99)
      print ("%s: %s is producing %d to the queue!" % (time.ctime(), self.getName(), randomnum))
      self.data.put(randomnum) #将数据依次存入队列
      time.sleep(1)
    print ("%s: %s finished!" %(time.ctime(), self.getName()))

#Consumer thread
class Consumer_even(threading.Thread):
  def __init__(self,t_name,queue):
    threading.Thread.__init__(self,name=t_name)
    self.data=queue
  def run(self):
    while 1:
      try:
        val_even = self.data.get(1,5) #get(self, block=True, timeout=None) ,1就是阻塞等待,5是超时5秒
        if val_even%2==0:
          print ("%s: %s is consuming. %d in the queue is consumed!" % (time.ctime(),self.getName(),val_even))
          time.sleep(2)
        else:
          self.data.put(val_even)
          time.sleep(2)
      except:   #等待输入,超过5秒 就报异常
        print ("%s: %s finished!" %(time.ctime(),self.getName()))
        break
class Consumer_odd(threading.Thread):
  def __init__(self,t_name,queue):
    threading.Thread.__init__(self, name=t_name)
    self.data=queue
  def run(self):
    while 1:
      try:
        val_odd = self.data.get(1,5)
        if val_odd%2!=0:
          print ("%s: %s is consuming. %d in the queue is consumed!" % (time.ctime(), self.getName(), val_odd))
          time.sleep(2)
        else:
          self.data.put(val_odd)
          time.sleep(2)
      except:
        print ("%s: %s finished!" % (time.ctime(), self.getName()))
        break
#Main thread
def main():
  queue = Queue()
  producer = Producer('Pro.', queue)
  consumer_even = Consumer_even('Con_even.', queue)
  consumer_odd = Consumer_odd('Con_odd.',queue)
  producer.start()
  consumer_even.start()
  consumer_odd.start()
  producer.join()
  consumer_even.join()
  consumer_odd.join()
  print ('All threads terminate!')

if __name__ == '__main__':
  main()
View Code

线程不安全实例:

import threading,time

li=[1,2,3,4,5]

def pri():
    while li:
        a=li[-1]
        print(a)
        time.sleep(1)
        try:
            li.remove(a)
        except:
            print('----',a)

t1=threading.Thread(target=pri,args=())
t1.start()
t2=threading.Thread(target=pri,args=())
t2.start()
View Code
原文地址:https://www.cnblogs.com/louhui/p/7944583.html