Py西游攻关之四线程,线程中互斥锁

线程(下)

线程的概念

线程是操作系统能够进行演算调度的微乎其微单位。它被含有在进度中。是进度中的实际运作单位。一条线程指的是进程中一个单纯顺序的调控流。四个历程中得以并发多少个线程,每条线程并行实施差异的职责
八个线程的推行会通过线程的调度去抢占CPU的财富

线程与经过

1、同步锁 (Lock)

  当全局能源(counter)被侵占的情形,难点发生的来由就是从未调节五个线程对同样能源的拜访,对数码变成损坏,使得线程运转的结果不可预期。那种意况叫做“线程不安全”。在支付进程中我们务必要幸免那种景况,这怎么制止?那就用到了互斥锁了。

例如:

 1 import threading,time
 2 def sub():
 3     global num         #对全局变量进行操作
 4 
 5     temp=num
 6     time.sleep(0.001)    #模拟线程执行中出现I/o延迟等
 7     num=temp-1           #所有线程对全局变量进行减一
 8 
 9     time.sleep(1)
10 
11 num=100
12 l=[]
13 
14 for i in range(100):
15     t=threading.Thread(target=sub,args=())
16     t.start()
17     l.append(t)
18 
19 for obj in l:
20     obj.join()
21 
22 print(num)          
23 
24 #执行结果不可预期:
25 >>:90
26 >>:93
27 >>:92
28 

7.同步锁

本条例子很杰出,实话说,这几个事例作者是直接照搬前辈的,并不是原创,然而确实也很有趣,请看:

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva

import threading,time

number = 100
def subnum():
    global number
    number -= 1

threads = []
for i in range(100):
    t = threading.Thread(target=subnum,args=[])
    t.start()
    threads.append(t)

for i in threads:
    i.join()

print(number)

 

那段代码的情趣是,用九17个线程去减一,以此让变量number为十0的变为0

 

结果:

 

亚洲必赢官网 1

 

那么本人有点的改下代码看看: 

 

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva

import threading,time

number = 100
def subnum():
    global number
    temp = number
    time.sleep(0.2)
    number = temp -1

threads = []
for i in range(100):
    t = threading.Thread(target=subnum,args=[])
    t.start()
    threads.append(t)

for i in threads:
    i.join()

print(number)

  

并从未相当的大的更改对吗,只是加了3个一时半刻变量,并且中途抛锚了0.2s而已。

而这一个结果就不雷同了:

亚洲必赢官网 2

 

此地本身先说下,time.sleep(0.二)是自己故意加的,正是要显示那么些效应,倘若你的计算机不加sleep就已经出现那一个情景了那么你就不要加了,那咋回事呢?那正是线程共用多少的潜在危急性,因为线程都是抢着CPU财富在运行,只要发现成空儿就分别抢着跑,所以在那停顿的0.2s时间中,就会有新的线程抢到机会开端运维,那么九十多个线程就有9十多个线程在抢机会运营,抢到的时刻都以在temp还尚未减壹的值,也正是十0,所以大多数的线程都抢到了100,然后减一,少1些线程没抢到,抢到已经减了三次的99,那正是为什么会是99的来由。而那些抢占的年月和结果并不是素有的案由,究其一直依旧因为Computer的布局难题了,配置越好的话,那种越不易于发生,因为2个线程抢到CPU能源后平昔在运作,别的的线程在短短的年华里得不到机会。

 

而为什么number -= 一,不依靠别的变量的写法就没事吧?因为numebr -=
1其实是七个步骤,减一仁同一视复赋值给number,那些动作太快,所以根本没给别的的线程机会。

 

图解: 

亚洲必赢官网 3

 

那么这几个主题材料大家怎么消除吗,在随后的成本中相对会遇见那种景色对啊,这一个能够缓解吧?依照上边包车型客车教师,有人会想到用join,而眼下早已提过了join会使10二线程变成串行,失去了三十二线程的打算。这几个到底怎么解决吗,用同步锁

同步锁:当运维起来加锁,幸免其余线程索取,当运转结束释放锁,让别的线程继续

 

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva
import threading,time

r = threading.Lock() #创建同步锁对象

number = 100
def subnum():
    global number
    r.acquire() #加锁
    temp = number
    time.sleep(0.2)
    number = temp - 1
    r.release() #释放


threads = []
for i in range(100):
    t = threading.Thread(target=subnum,args=[])
    t.start()
    threads.append(t)

for i in threads:
    i.join()

print(number)

  

运作结果:

亚洲必赢官网 4

 

只是你意识没,这一个运转太慢了,每一个线程都运作了二回sleep,竟然又成为和串行运转大概了对啊?但是仍旧和串行稍微有点分化,只是在有一块锁那里是串行,在别的地点或许二十多线程的作用

 

这正是说有情侣要问了,既然都是锁,已经有了贰个GIL,那么还要一齐锁来干嘛呢?一句话,GIL是重视于保障线程安全,同步锁是用户级的可控机制,开荒中防止那种不分明的心腹隐患

 

经过的定义

程序实施的实例称为进度
亚洲必赢官网 ,每一种进度提供施行顺序所需的财富。进度具备虚拟地址空间,可实行代码,系统对象的展开句柄,安全上下文,唯一进度标志符,环境变量,优先级档次,最小和最大职业集。每一个过程都选择单线程运营,平时号称主线程,但足以从其任何线程创制其它线程

进度和线程的相比
进度和线程之间的可比是绝非意思的,因为经过是3个程序的推行实例,而经过是由线程举行施行的,但线程和进度终归依然三种体制

  • 进程可以创设子进度,而种种子进度又有啥不可开三个线程
  • 线程之间可以共享数据,而线程之间不可能共享数据,线程之间能够拓展通讯,而经过之间张开通讯就会比较费心
  • 开荒进程要比开垦线程的开支大过多

怎么是线程(thread)?

线程是操作系统能够进行演算调度的细微单位。它被含有在经过之中,是经过中的实际运转单位。一条线程指的是进程中一个单纯顺序的调整流,2个进程中得以并发七个线程,每条线程并行实行差异的天职

A thread is an execution context, which is all the information a CPU
needs to execute a stream of instructions.

Suppose you’re reading a book, and you want to take a break right now,
but you want to be able to come back and resume reading from the exact
point where you stopped. One way to achieve that is by jotting down the
page number, line number, and word number. So your execution context for
reading a book is these 3 numbers.

If you have a roommate, and she’s using the same technique, she can take
the book while you’re not using it, and resume reading from where she
stopped. Then you can take it back, and resume it from where you were.

Threads work in the same way. A CPU is giving you the illusion that it’s
doing multiple computations at the same time. It does that by spending a
bit of time on each computation. It can do that because it has an
execution context for each computation. Just like you can share a book
with your friend, many tasks can share a CPU.

On a more technical level, an execution context (therefore a thread)
consists of the values of the CPU’s registers.

Last: threads are different from processes. A thread is a context of
execution, while a process is a bunch of resources associated with a
computation. A process can have one or many threads.

Clarification: the resources associated with a process include memory
pages (all the threads in a process have the same view of the memory),
file descriptors (e.g., open sockets), and security credentials (e.g.,
the ID of the user who started the process).

互斥锁概念

  Python编制程序中,引进了对象互斥锁的定义,来确认保证加利亚共产党享数据操作的完整性。种种对象都对应于一个可称为”
互斥锁”
的符号,那几个标识用来保险在任一时刻,只好有一个线程访问该对象。在Python中大家接纳threading模块提供的Lock类。

  大家对上面的主次开始展览整顿改进,为此大家须要丰裕二个排斥锁变量lock =
threading.Lock(),然后在战斗财富的时候前边我们会先抢占那把锁lock.acquire(),对能源选取完了之后大家在出狱那把锁mutex.release()。

代码如下:

import threading,time
def sub():
    global num

    lock.acquire()
    temp=num
    time.sleep(0.01)
    num=temp-1
    lock.release()

    time.sleep(1)

num=100
l=[]
lock=threading.Lock()
for i in range(100):
    t=threading.Thread(target=sub,args=())
    t.start()
    l.append(t)

for obj in l:
    obj.join()

print(num)

 捌.死锁现象/可选用锁

前方既然已经用了1只锁,那么相信在随后的支出中,相对会用到应用八个同步锁的时候,所以那里模拟一下运用三个联合锁,看看会有如何处境发生

 

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva
import threading,time

a = threading.Lock() #创建同步锁对象a
b = threading.Lock() #创建同步锁对象b

def demo1():
    a.acquire() #加锁
    print('threading model test A....')
    b.acquire()
    time.sleep(0.2)
    print('threading model test B....')
    b.release()
    a.release() #释放

def demo2():
    b.acquire() #加锁
    print('threading model test B....')
    a.acquire()
    time.sleep(0.2)
    print('threading model test A....')
    a.release()
    b.release() #释放

threads = []
for i in range(5):
    t1 = threading.Thread(target=demo1,args=[])
    t2 = threading.Thread(target=demo2,args=[])
    t1.start()
    t2.start()
    threads.append(t1)
    threads.append(t2)

for i in threads:
    i.join()

 

  

运维结果:

亚洲必赢官网 5

 

此间就径直阻塞住了,因为demo一函数用的锁是外围a锁,内层b锁,demo2函数刚好相反,外层b锁,内层a锁,所以当十2线程运营时,五个函数同时在互抢锁,哪个人也不让何人,那就变成了绿灯,这几个阻塞现象又叫死锁现象。

 

那正是说为了防止发生那种事,我们可以动用threading模块下的陆风X八LOCK来创设重用锁依此来幸免这种境况

 

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva
import threading,time

r = threading.RLock() #创建重用锁对象

def demo1():
    r.acquire() #加锁
    print('threading model test A....')
    r.acquire()
    time.sleep(0.2)
    print('threading model test B....')
    r.release()
    r.release() #释放

def demo2():
    r.acquire() #加锁
    print('threading model test B....')
    r.acquire()
    time.sleep(0.2)
    print('threading model test A....')
    r.release()
    r.release() #释放

threads = []
for i in range(5):
    t1 = threading.Thread(target=demo1,args=[])
    t2 = threading.Thread(target=demo2,args=[])
    t1.start()
    t2.start()
    threads.append(t1)
    threads.append(t2)

for i in threads:
    i.join()

  

运作结果:

亚洲必赢官网 6

 

以此汉兰达lock其实便是Lock+总计器,总括器里的开端值为0,每嵌套一层锁,总结器值加1,每释放一层锁,总结器值减一,和共同锁同样,只有当班值日为0时才算身故,让其余线程接着抢着运转。而这么些Tucsonlock也有叁个合法一点的名字,递归锁

 

 那么推测有朋友会问了,为何会有死锁现象吧?可能您应该问,是何许生产环境导致有死锁现象的,依旧那句,为了掩护数量同步性,幸免八线程操作同一数据时产生争执。那些说辞很笼统对吧,笔者说细点。比如后面包车型客车购物车系统,即使大家在操作数据时又再度取了一回数据来保障数据的真正,假使多少个用户同时登入购物车系统在操作的话,或然差异的操作但会波及到同二个数码的时候,就会促成数据大概分化台了,那么就能够在其间代码里加三遍联合锁,然后再在事实上操作处再加一回联袂锁,那样就出现多层同步锁,那么也就会合世死锁现象了,而那时那一个死锁现象是大家付出中恰恰必要的。

自身想,说了那一个事例你应该能够领略为啥lock里还要有lock,很轻松形成死锁现象我们照旧要用它了,简单来说假诺急需死锁现象就用1道锁,不需求就换到递归锁。

 

Python中开创线程

Python中开创线程有各个格局

何以是经过(process)?

An executing instance of a program is called a process.

Each process provides the resources needed to execute a program. A
process has a virtual address space, executable code, open handles to
system objects, a security context, a unique process identifier,
environment variables, a priority class, minimum and maximum working set
sizes, and at least one thread of execution. Each process is started
with a single thread, often called the primary thread, but can create
additional threads from any of its threads.

二、死锁与递归锁

  所谓死锁:
是指八个或三个以上的长河或线程在实行进程中,因争夺财富而招致的1种相互等待的处境,若无外力效用,它们都将无法推进下去。此时称系统处于死锁状态或系统一发布出了死锁,那些恒久在互相等待的长河称为死锁进程。 

会生出死锁的例子:

class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        self.foo()


    def foo(self):
        LockA.acquire()
        print('I am %s GET LOCKA---------%s'%(self.name,time.ctime()))
        LockB.acquire()
        print('I am %s GET LOCKB---------%s' % (self.name, time.ctime()))

        LockB.release()

        LockA.release()

LockA=threading.Lock()
LockB=threading.Lock()

for i in range(10):
    t=MyThread()
    t.start()

 玖.时限信号量/绑定式时限信号量

能量信号量也是3个线程锁

1)Semaphore

非功率信号量以为更有保有四线程的含义。先不急着说,看看例子就懂:

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva
import threading,time

s = threading.Semaphore(3) #创建值为3的信号量对象

def demo():
    s.acquire() #加锁
    print('threading model test A....')
    time.sleep(2)
    s.release() #释放

threads = []
for i in range(10):
    t = threading.Thread(target=demo,args=[])
    t.start()
    threads.append(t)

for i in threads:
    i.join()

  

运作结果:

亚洲必赢官网 7

 

假使您亲自测试那段代码,你会发觉,这一个结果是三个1组出的,出了1次叁个一组的,最终出了三个一组,贰个一组都以相互的,中间停顿二秒。

那边能够给很形象的例子,如若有些地方的停车位只可以同时停叁辆车,当停车位有空时其余的车才方可停进来。那里的一个停车位就一定于复信号量。

 

2)BoundedSemaphore

既是有信号量为我们成功这一个一组一组的操作结果,但敢不敢保险那个线程就不会冷不丁的越出这些设定好的车位呢?比如设定好的三个时域信号量一组,大家都晓得线程是争强着运转,万一就有除了设定的1个线程外的壹多少个线程抢到了运维权,哪个人也不让何人,正是要共同运转吧?好比,那里唯有贰个车位,已经停满了,但有人正是要去挤1挤,出现第四辆或许第伍辆车的场馆,这些和现实生活中的例子几乎太适宜了对吗?

那么大家怎么做?当然这一个问题已经有人想好了,所以有了时域信号量的进步版——绑定式频域信号量(BoundedSemaphore)。既然是升格版,那么同时限信号量同样该有的都有个别,用法也如出一辙,正是有个功用,在设定好的多少个线程1组运转时,假设有别的线程也抢到运维权,那么就会报错

比如thread_Py西游攻关之四线程,线程中互斥锁。lock =
threading.BoundedSemaphore(5),那么四线程同时运行的线程数就不能不在5以内(包涵伍),不然就报错。换句话,它具有了实时监察和控制的作用,好比停车位上的爱抚,要是发现车位满了,就不准放行车辆,直到有空位了再允许车辆进入停车。

因为那些异常粗略,就多了个监督检查职能,别的和semaphore一样的用法,小编就不演示了,本人雕刻吧

 

threading 模块

进度与线程的区分?

  1. Threads share the address space of the process that created it;
    processes have their own address space.
  2. Threads have direct access to the data segment of its process;
    processes have their own copy of the data segment of the parent
    process.
  3. Threads can directly communicate with other threads of its process;
    processes must use interprocess communication to communicate with
    sibling processes.
  4. New threads are easily created; new processes require duplication of
    the parent process.
  5. Threads can exercise considerable control over threads of the same
    process; processes can only exercise control over child processes.
  6. Changes to the main thread (cancellation, priority change, etc.) may
    affect the behavior of the other threads of the process; changes to
    the parent process does not affect child processes.

利用递归锁化解:

  在Python中为了帮衬在同一线程中往往呼吁同1财富,python提供了可重入锁LX570Lock。那一个SportageLock内部维护着三个Lock和3个counter变量,counter记录了acquire的次数,从而使得能源得以被频仍require。直到3个线程全数的acquire都被release,别的的线程本事获取财富。下边包车型大巴事例假如运用LX570Lock取代Lock,则不会生出死锁:

class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        self.foo()
        self.bar()

    def foo(self):
        RLock.acquire()
        print('I am %s GET LOCKA---------%s'%(self.name,time.ctime()))
        RLock.acquire()
        print('I am %s GET LOCKB---------%s' % (self.name, time.ctime()))

        RLock.release()
        RLock.release()

    def bar(self):

        RLock.acquire()
        print('I am %s GET LOCKB---------%s' % (self.name, time.ctime()))
        time.sleep(1)
        RLock.acquire()
        print('I am %s GET LOCKA---------%s' % (self.name, time.ctime()))

        RLock.release()
        RLock.release()

RLock=threading.RLock()

for i in range(10):
    t=MyThread()
    t.start()

  

十.规则变量同步锁

不多说,它也是1个线程锁,本质上是在CR-Vlock基础之上再增添下边包车型地铁八个艺术 

condition = threading.Condition([Lock/RLock]),暗许里面包车型客车参数是昂Coralock

 

wait():条件不满足时调用,释放线程并进入等待绿灯

notify():条件创立后调用,文告等待池激活一个线程

notifyall():条件成立后调用,布告等待池激活全体线程

 

一贯上例子

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva
import threading,time
from random import randint

class producer(threading.Thread):
    '''
    生产者
    '''
    def run(self):
        global Li
        while True:
            value = randint(0,100) #创建一百以内随机数
            print('生产者',self.name,'Append:'+str(value),Li)
            if con.acquire(): #加锁
                Li.append(value) #把产品加入产品列表里
                con.notify()  #通知等待池里的消费者线程激活并运行
                con.release() #释放
            time.sleep(3)     #每3秒做一次产品

class consumer(threading.Thread):
    '''
    消费者
    '''
    def run(self):
        global Li
        while True:
            con.acquire() #获取条件变量锁,必须和生产者同一个锁对象,生产者通知后在此处开始运行
            if len(Li) == 0: #如果产品列表内没数据,表示消费者先抢到线程运行权
                con.wait()   #阻塞状态,等待生产者线程通知
            print('消费者',self.name,'Delete:'+str(Li [0]),Li)
            Li.remove(Li[0]) #删除被消费者用掉的产品
            con.release()    #释放
            time.sleep(0.5)  #每0.5秒用掉一个产品

con = threading.Condition() #创建条件变量锁对象
threads = [] #线程列表
Li = [] #产品列表

for i in range(5):
    threads.append(producer())

threads.append(consumer())

for i in threads:
    i.start()

for i in threads:
    i.join()

  

运维结果:

亚洲必赢官网 8

 

图表只截取了一部分,因为它平素在无线循环着的。那些生产者和消费者的模子很突出,必须精晓,种种步骤分别什么看头我都注释了,不再赘言了。

 

直接调用threading模块 创设线程

Python中制造线程能够行使threading模块

  • threading.Thread(target=func,args = params,) 创造线程
    target钦命推行的函数 target内定参数元组格局

'''
python thread
'''
import threading

import time

beggin = time.time()


def foo(n):
    print('foo%s' % n)
    time.sleep(1)


def bar(n):
    print('bar %s' % n)


end = time.time()
cast_time = end - beggin
print(float(cast_time))
# 创建线程
t1 = threading.Thread(target=foo, args=('thread1',))
t2 = threading.Thread(target=bar, args=('thread2',))
t1.start()
t2.start()

Python GIL(Global Interpreter Lock) 

CPython implementation detail: In CPython, due to the Global Interpreter
Lock, only one thread can execute Python code at once (even though
certain performance-oriented libraries might overcome this limitation).
If you want your application to make better use of the computational
resources of multi-core machines, you are advised to use
multiprocessing. However, threading is still an appropriate model if you
want to run multiple I/O-bound tasks simultaneously.

3、Semaphore(信号量)

Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+一;
计数器无法小于0;当计数器为0时,acquire()将卡住线程直到别的线程调用release()。

实例:(同时只有八个线程能够博得semaphore,即能够界定最辛辛这提接数为五):

 1 import threading
 2 import time
 3 
 4 semaphore = threading.Semaphore(5)
 5 
 6 def func():
 7     if semaphore.acquire():
 8         print (threading.currentThread().getName() + ' get semaphore')
 9         time.sleep(2)
10         semaphore.release()
11 
12 for i in range(20):
13   t1 = threading.Thread(target=func)
14   t1.start()

 

11.event事件

 类似于condition,但它并不是三个线程锁,并且未有锁的效力

event = threading.伊夫nt(),条件环境指标,初步值为False

 

event.isSet():重临event的境况值

event.wait():如果event.isSet()的值为False将阻塞

event.set():设置event的动静值为True,全体阻塞池的线程激活并跻身就绪状态,等待操作系统调度

event.clear():恢复生机event的意况值False

 

不多说,看2个例证:

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva

import threading,time

class boss(threading.Thread):
    def run(self):
        print('boss:今晚加班!')
        event.isSet() or event.set() #设置为True
        time.sleep(5)   #切换到员工线程
        print('boss:可以下班了')
        event.isSet() or event.set() #又设置为True


class worker(threading.Thread):
    def run(self):
        event.wait() #等待老板发话,只有值为True再往下走
        print('worker:唉~~~,又加班')
        time.sleep(1) #开始加班
        event.clear() #设置标志为false
        event.wait()  #等老板发话
        print('worker:oh yeah,终于可以回家了')


event = threading.Event()
threads = []
for i in range(5):
    threads.append(worker())
threads.append(boss())

for i in threads:
    i.start()

for i in threads:
    i.join()

  

 

运转结果:

亚洲必赢官网 9

 

实际这几个和condition的通讯原理是壹模同样的,只是condition用的是notify,event用的set和isset

经过持续threading模块调用线程

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()
  • 创制类承袭threading.Thread
  • 重写类的run方法

threading模块

4、Event对象

  线程的三个器重本性是每一个线程都以单独运营且情形不行预测。假诺程序中的其余线程须要经过推断某些线程的图景来规定本身下一步的操作,那时线程同步难点就
会变得老大勤奋。

  为了消除这一个标题,我们须求选用threading库中的伊夫nt对象。伊夫nt对象涵盖三个可由线程设置的信号标识,它同意线程等待有些事件的产生。

  在
开首情状下,伊芙nt对象中的实信号标识被安装为假。假如有线程等待1个伊芙nt对象,
而那个伊夫nt对象的标识为假,那么那个线程将会被一向不通直至该标记为真。3个线程借使将二个伊夫nt对象的功率信号标识设置为真,它将唤起全部等待这一个伊芙nt对象的线程。假如贰个线程等待1个早已被安装为实在伊芙nt对象,那么它将忽略那几个事件,
继续推行

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

event.wait():如果 event.isSet()==False将阻塞线程;

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

event.clear():恢复event的状态值为False。

 

用evnt对象模拟红绿灯:

import queue,threading,time
import random

event = threading.Event()
def light():
    while True:
        event.set()
        for i in range(10):
            print('light green')
            time.sleep(1)
        event.clear()
        for i in range(10,13):
            print('light yellow')
            time.sleep(1)
        for i in range(13,21):
            print('light red')
            time.sleep(1)

def car(i):
    while True:
        time.sleep(random.randint(1,5))
        if event.isSet():
            print('car %s is runing'%i)
        else:
            print('car %s is waiting'%i)

if __name__ == '__main__':
    l1=threading.Thread(target=light)
    l1.start()

    for i in range(5):
        i = threading.Thread(target=car,args=(i,))
        i.start()

 

Python 八线程中的GIL

Python的GIL并不是Python的特征,它是在完毕Python解析器也正是根据C语言的解析器
CPython时所引进的二个概念。Python能够用不相同的编写翻译器来编写翻译成可举办代码。例如C语言中的GCC等。也正是说唯有在CPython中才会合世GIL的景色
GIL又叫做全局解释器锁(Global Interpreter Lock)
当代的CPU已经是多核CPU,为了更有效的施用多核处理器的性质,就出现了10二线程的编制程序格局。而在消除多线程之间数据完整性和气象同步的最轻易易行的主意就是加锁。GIL正是给Python解释器加了一把大锁。大家清楚Python是由解释器实行的,由于GIL的留存
只可以有二个线程被解释器试行,那样就使得Python在十2线程推行上的成效变低。由于历史遗留难点,发现大批量库代码开采者现已重度重视GIL而万分麻烦去除了。也正是说在多核CPU上,并行施行的Python八线程,甚至不及串行施行的Python程序,那正是GIL存在的标题

一 线程的二种调用格局

一向调用

实例1:

亚洲必赢官网 10

亚洲必赢官网 11

import threading
import time

def sayhi(num): #定义每个线程要运行的函数

    print("running on number:%s" %num)

    time.sleep(3)

if __name__ == '__main__':

    t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
    t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例

    t1.start() #启动线程
    t2.start() #启动另一个线程

    print(t1.getName()) #获取线程名
    print(t2.getName())

亚洲必赢官网 12

承继式调用:

亚洲必赢官网 13

亚洲必赢官网 14

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()

亚洲必赢官网 15

4、队列(queue)

”’

开创三个“队列”对象

import queueq
q = queue.Queue(maxsize = 10)
    #queue.Queue类正是贰个行列的一道达成。队列长度可为Infiniti大概轻易。可通过Queue的构造函数的可选参数
    #maxsize来设定队列长度。假若maxsize小于一就表示队列长度Infiniti。

q.put()    将叁个值22放入队列中

    #调用队列对象的put()方法在队尾插入3个品类。put()有五个参数,第四个item为供给的,为插入项目标值;第③个block为可选参数,默感到一。假如队列当前为空且block为一,put()方法就使调用线程暂停,直到空出三个数码单元。假使block为0,put方法将引发Full卓殊。

q.get()    将四个值从队列中收取    

    #调用队列对象的get()方法从队头删除并赶回一个品类。可选参数为block,默许为True。借使队列为空且block为True,get()就使调用线程暂停,直至有档次可用。要是队列为空且block为False,队列将引发Empty万分。

”’

queue的常用方法

'''

此包中的常用方法(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() 实际上意味着等到队列为空,再执行别的操作

'''

*12.队列(queue)

实为上,队列是三个数据结构。

 

1)创制一个“队列”对象
import Queue
q = Queue.Queue(maxsize = 10)
Queue.Queue类就是三个体系的联手实现。队列长度可为Infiniti也许个别。可透过Queue的构造函数的可选参数maxsize来设定队列长度。假诺maxsize小于1就表示队列长度Infiniti。

二)将三个值放入队列中
q.put(obj)
调用队列对象的put()方法在队尾插入三个门类。put()有八个参数,第叁个item为要求的,为插入项目标值;第三个block为可选参数,默认为
一。假使队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数量单元。假使block为0,put方法将抓住Full卓殊。

三)将一个值从队列中抽出
q.get()
调用队列对象的get()方法从队头删除并重返三个项目。可选参数为block,默感到True。假诺队列为空且block为True,get()就使调用线程暂停,直至有项目可用。假如队列为空且block为False,队列将引发Empty卓殊。

 

例:

亚洲必赢官网 16

 

 

4)Python Queue模块有二种队列及构造函数:

  • Python Queue模块的FIFO队列先进先出    class queue.Queue(maxsize)
  • LIFO类似于堆,即先进后出        class
    queue.LifoQueue(maxsize)
  • 还有一种是事先级队列等第越低越先出来  class
    queue.PriorityQueue(maxsize)

 

当maxsize值比put的数量少时就会阻塞住,当数码被get后留有空间才具随着put进去,就如于线程的时域信号量

亚洲必赢官网 17

 

 

5)queue中的常用方法(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_nowait(item):相当q.put(item, False)
q.task_done():在成就①项职业以往,q.task_done()
函数向职分现已到位的行列发送一个时域信号
q.join():实际上意味着等到队列为空,再实践别的操作

 

6)队列有怎样利益,与列表差异

队列自身就有①把锁,内部已经维持一把锁,假设您用列表的话,当环境是在十二线程下,那么列表数据就必定会有争辨,而队列不会,因为此,队列有个小名——二十多线程利器

例:

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva

import threading,time
import queue
from random import randint

class productor(threading.Thread):
    def run(self):
        while True:
            r = randint(0,100)
            q.put(r)
            print('生产出来 %s 号产品'%r)
            time.sleep(1)

class consumer(threading.Thread):
    def run(self):
        while True:
            result =q.get()
            print('用掉 %s 号产品'%result)
            time.sleep(1)

q = queue.Queue(10)
threads = []
for i in range(3):
    threads.append(productor())

threads.append(consumer())

for i in threads:
    i.start()

  

运营结果:

亚洲必赢官网 18

 

那里素有不用加锁就马到功成了前头的劳动者消费者模型,因为queue里面自带了壹把锁。

 

好的,关于线程的知识点,批注完。

 

Python GIL的出现气象

在Python中就算职分是IO密集型的,能够行使四线程。而且Python的四线程相当擅长处理那种主题材料
而1旦Python中要是职责是一个钱打二拾伍个结密集型的,就必要处理一下GIL

二 Join & Daemon

亚洲必赢官网 19

亚洲必赢官网 20

import threading
from time import ctime,sleep
import time

def music(func):
    for i in range(2):
        print ("Begin listening to %s. %s" %(func,ctime()))
        sleep(4)
        print("end listening %s"%ctime())

def move(func):
    for i in range(2):
        print ("Begin watching at the %s! %s" %(func,ctime()))
        sleep(5)
        print('end watching %s'%ctime())

threads = []
t1 = threading.Thread(target=music,args=('七里香',))
threads.append(t1)
t2 = threading.Thread(target=move,args=('阿甘正传',))
threads.append(t2)

if __name__ == '__main__':

    for t in threads:
        # t.setDaemon(True)
        t.start()
        # t.join()
    # t1.join()
    t2.join()########考虑这三种join位置下的结果?
    print ("all over %s" %ctime())

亚洲必赢官网 21

setDaemon(True):

      将线程注脚为守护线程,必须在start() 方法调用在此之前安装,
假若不安装为护理线程程序会被Infiniti挂起。那一个措施基本和join是相反的。当我们在程序运维中,推行多个主线程,假若主线程再次创下建1个子线程,主线程和子线程
就分兵两路,分别运行,那么当主线程完结想淡出时,会查验子线程是还是不是成功。假诺子线程未成功,则主线程会等待子线程达成后再脱离。可是有时我们必要的是
只要主线程完结了,不管敬仲线程是或不是做到,都要和主线程一同退出,那时就能够用setDaemon方法啦 

join():

       在子线程实现运转在此以前,这么些子线程的父线程将一向被封堵。

别的方式

亚洲必赢官网 22

亚洲必赢官网 23

thread 模块提供的其他方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
# 除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
# run(): 用以表示线程活动的方法。
# start():启动线程活动。
# join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。

亚洲必赢官网 24

join与task_done方法

'''
join() 阻塞进程,直到所有任务完成,需要配合另一个方法task_done。

    def join(self):
     with self.all_tasks_done:
      while self.unfinished_tasks:
       self.all_tasks_done.wait()

task_done() 表示某个任务完成。每一条get语句后需要一条task_done。


import queue
q = queue.Queue(5)
q.put(10)
q.put(20)
print(q.get())
q.task_done()
print(q.get())
q.task_done()

q.join()

print("ending!")
'''

四线程式爬虫

有个别朋友学完线程还不明了线程到底能接纳于怎么着生活实际,好的,不多说,来,大家爬下堆糖网()的校花照片。

 

import requests
import urllib.parse
import threading,time,os

#设置照片存放路径
os.mkdir('duitangpic')
base_path = os.path.join(os.path.dirname(__file__),'duitangpic')

#设置最大信号量线程锁
thread_lock=threading.BoundedSemaphore(value=10)

#通过url获取数据
def get_page(url):
    header={'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}
    page=requests.get(url,headers=header)
    page=page.content #content是byte
    #转为字符串
    page=page.decode('utf-8')
    return page

#label  即是搜索关键词
def page_from_duitang(label):
    pages=[]
    url='https://www.duitang.com/napi/blog/list/by_search/?kw={}&start={}&limit=1000'
    label=urllib.parse.quote(label)#将中文转成url(ASCII)编码
    for index in range(0,3600,100):
        u=url.format(label,index)
        #print(u)
        page=get_page(u)
        pages.append(page)
    return pages

def findall_in_page(page,startpart,endpart):
    all_strings=[]
    end=0
    while page.find(startpart,end) !=-1:
        start=page.find(startpart,end)+len(startpart)
        end=page.find(endpart,start)
        string=page[start:end]
        all_strings.append(string)

    return all_strings

def pic_urls_from_pages(pages):
    pic_urls=[]
    for page in pages:
        urls=findall_in_page(page,'path":"','"')
        #print('urls',urls)
        pic_urls.extend(urls)
    return pic_urls

def download_pics(url,n):
    header={'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}
    r=requests.get(url,headers=header)
    path=base_path+'/'+str(n)+'.jpg'
    with open(path,'wb') as f:
        f.write(r.content)
    #下载完,解锁
    thread_lock.release()

def main(label):
    pages=page_from_duitang(label)
    pic_urls=pic_urls_from_pages(pages)
    n=0
    for url in pic_urls:
        n+=1
        print('正在下载第{}张图片'.format(n))
        #上锁
        thread_lock.acquire()
        t=threading.Thread(target=download_pics,args=(url,n))
        t.start()
main('校花')

  

运作结果:

亚洲必赢官网 25

 

在与本py文件壹律的目录下,有个duitangpic的公文夹,张开看看:

亚洲必赢官网 26

 

 全是月宫仙子,而且不出意外又好几千张呢,笔者那唯有一千多张是因为小编手动结束了py程序运行,究竟本身这是出现说法,不须要真正等程序运营完。作者概略揣度,不出意外应该能爬到2000张左右的照片

 

哪些,老铁,得劲不?刺不激情?感受到多线程的用途了不?而且这照旧python下的伪多线程(IO密集型,但并不到底真正含义上的十二线程),你用别样的语言来爬更加精神。

 

join 和daemon

join

  • 在子线程完结运行以前,这些子线程的父线程将一直被打断。在四个先后中大家实行一个主线程,这一个主线程更创立一个子线程,主线程和子线程就互相推行,当子线程在主线程中调用join方法时,主线程会等待子线程施行完后再结束

'''in main thread'''
t.join() 主线程会等待线程t执行完成后再继续执行

daemon

  • setDaemon(true)
    将线程注脚为护理线程,必须在start() 方法调用在此以前安装,
    倘诺不安装为看护线程程序会被Infiniti挂起。那些法子基本和join是相反的。当大家在程序运转中,推行二个主线程,要是主线程再次创下办贰个子线程,主线程和子线程
    就分兵两路,分别运维,那么当主线程完结想退出时,会核查子线程是不是到位。要是子线程未产生,则主线程会等待子线程实现后再脱离。不过有时大家须求的是
    只要主线程完毕了,不管仲线程是还是不是成功,都要和主线程一同退出,那时就足以
    用setDaemon方法啦
  • currentThread() 获取当前进行的线程

三 同步锁(Lock)

亚洲必赢官网 27

import time
import threading

def addNum():
    global num #在每个线程中都获取这个全局变量
    # num-=1

    temp=num
    print('--get num:',num )
    #time.sleep(0.1)
    num =temp-1 #对此公共变量进行-1操作


num = 100  #设定一个共享变量
thread_list = []
for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: #等待所有线程执行完毕
    t.join()

print('final num:', num )

亚洲必赢官网 28

 

 

亚洲必赢官网 29

 

注意:

一:  why num-=一没难点呢?这是因为动作太快(完结这些动作在切换的小时内)

二: if
sleep(1),现象会更明了,玖拾肆个线程每1个必然都未有实践完就进展了切换,大家说过sleep就等效于IO阻塞,1s以内不会再切换回来,所以最终的结果必然是9玖.

 

多少个线程都在同时操作同一个共享能源,所以变成了能源破坏,如何做呢?

有同学会想用join呗,但join会把整个线程给停住,形成了串行,失去了三十六线程的意义,而大家只须求把计算(涉及到操作公共数据)的时候串行实施。

大家得以经过同步锁来解决那种主题素材

亚洲必赢官网 30

import time
import threading

def addNum():
    global num #在每个线程中都获取这个全局变量
    # num-=1
    lock.acquire()
    temp=num
    print('--get num:',num )
    #time.sleep(0.1)
    num =temp-1 #对此公共变量进行-1操作
    lock.release()

num = 100  #设定一个共享变量
thread_list = []
lock=threading.Lock()

for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: #等待所有线程执行完毕
    t.join()

print('final num:', num )

亚洲必赢官网 31

难点解决,但

借问:同步锁与GIL的关系?

Python的线程在GIL的调整之下,线程之间,对全部python解释器,对python提供的C
API的访问都以排斥的,那能够用作是Python内核级的排外机制。可是那种互斥是我们不可能调节的,大家还索要别的壹种可控的排外机制———用户级互斥。内核级通过互斥爱护了基本的共享财富,同样,用户级互斥珍重了用户程序中的共享能源。

GIL
的成效是:对于四个解释器,只好有3个thread在实施bytecode。所以每时每刻唯有一条bytecode在被实行三个thread。GIL保证了bytecode
那层面上是thread safe的。
不过借使您有个操作比如 x +=
1,这一个操作须要多少个bytecodes操作,在实行那一个操作的多条bytecodes时期的时候大概中途就换thread了,那样就出现了data
races的景况了。

 

那自身的壹块儿锁也是承接保险平等时刻唯有三个线程被实行,是或不是从没有过GIL也得以?是的;那要GIL有啥鸟用?你没治;

queue的两种方式:

1、queue.Queue()  先进先出方式

二、queue.LifoQueue()    先进后出,类似栈

3、queue.PriorityQueue()  
优先级情势,优先级越高越先出,数字月中代表优先级越高

import queue

#######################先进后出
q=queue.LifoQueue()

q.put(34)
q.put(56)
q.put(12)

#####################优先级
q=queue.PriorityQueue()
q.put([5,100])
q.put([7,200])
q.put([3,"hello"])
q.put([4,{"name":"alex"}])

while 1:
  data=q.get()
  print(data)

线程中的锁

先看一个线程共享数据的主题材料

'''
线程安全问题
'''
# 定义一个共享变量
import threading

import time

num = 100


def sub():
    # 操作类变量
    global num
    tmp = num
    time.sleep(0.1)
    num = tmp - 1


if __name__ == '__main__':
    thread_list = []
    for i in range(100):
        t1 = threading.Thread(target=sub)
        t1.start()
        thread_list.append(t1)
    for i in range(100):
        t2 = thread_list[i]
        t2.join()

print('final num' + str(num))
>>> 
final num99

四 线程死锁和递归锁

     
在线程间共享七个能源的时候,假设八个线程分别攻克壹部分能源并且还要等待对方的能源,就会促成死锁,因为系统剖断那有的能源都正在利用,全部那四个线程在无外力成效下将直接等候下去。下边是二个死锁的事例:

亚洲必赢官网 32

亚洲必赢官网 33

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()#等待线程结束,后面再讲。

亚洲必赢官网 34

消除办法:使用递归锁,将

1
2
lockA=threading.Lock()
lockB=threading.Lock()<br>#--------------<br>lock=threading.RLock()

为了帮忙在同1线程中数十次伸手同1财富,python提供了“可重入锁”:threading.奥迪Q伍Lock。凯雷德Lock内部维护着二个Lock和一个counter变量,counter记录了acquire的次数,从而使得财富可以被数十一回acquire。直到一个线程全数的acquire都被release,别的的线程才具获取财富。

应用

亚洲必赢官网 35

亚洲必赢官网 36

import time

import threading

class Account:
    def __init__(self, _id, balance):
        self.id = _id
        self.balance = balance
        self.lock = threading.RLock()

    def withdraw(self, amount):

        with self.lock:
            self.balance -= amount

    def deposit(self, amount):
        with self.lock:
            self.balance += amount


    def drawcash(self, amount):#lock.acquire中嵌套lock.acquire的场景

        with self.lock:
            interest=0.05
            count=amount+amount*interest

            self.withdraw(count)


def transfer(_from, to, amount):

    #锁不可以加在这里 因为其他的其它线程执行的其它方法在不加锁的情况下数据同样是不安全的
     _from.withdraw(amount)

     to.deposit(amount)



alex = Account('alex',1000)
yuan = Account('yuan',1000)

t1=threading.Thread(target = transfer, args = (alex,yuan, 100))
t1.start()

t2=threading.Thread(target = transfer, args = (yuan,alex, 200))
t2.start()

t1.join()
t2.join()

print('>>>',alex.balance)
print('>>>',yuan.balance)

亚洲必赢官网 37

队列的利用:生产者消费者模型

  在线程世界里,生产者便是生育数量的线程,消费者正是消费数据的线程。在二十多线程开荒当中,倘若劳动者处理速度不慢,而顾客处理速度极慢,那么生产者就务须等待买主处理完,才具一而再生产数量。一样的道理,假若买主的处理本事超越生产者,那么消费者就亟须待产者。为了消除那些主题素材于是引进了劳动者和顾客方式。

  生产者消费者形式是透过2个器皿来消除劳动者和买主的强耦合难点。生产者和买主相互之间不直接通信,而透过阻塞队列来进展电视发表,所以生产者生产完数据未来并非等待顾客处理,直接扔给卡住队列,消费者不找生产者要多少,而是一向从绿灯队列里取,卡住队列就一定于三个缓冲区,平衡了劳动者和顾客的处理本领。

  那就像,在茶楼,大厨做好菜,不须求直接和客户交换,而是交由前台,而客户去饭菜也不须要不找厨神,直接去前台领取就可以,那也是3个结耦的进度。

import queue,threading,time
import random

q = queue.Queue(50)

def Producer():
    while True:
        if q.qsize() < 20:
            n = random.randint(1, 100)
            q.put(n)
            print(" has made baozi %s" % n)
            time.sleep(1)

def Consumer(id):
    while True:
         s = q.get()
         print("Consumer"+id+"has eat %s" % s)
         time.sleep(2)

for i in range(5):
    t1=threading.Thread(target=Producer,args=())
    t1.start()

for i in range(2):
    t=threading.Thread(target=Consumer,args=(str(i),))
    t.start()

  

  

 

分析

上边的次第中,大家想要的是开启九十四个线程,每种线程将共享数据减去一,可是大家发现
输出的结果是9九,那种情形是因为十2线程在cpu中实践时是抢占式的,程序在初阶施行时,开启了九二十个线程去奉行,当程序实行到time.sleep(0.1)时,由于发生了线程的梗塞,所以cpu进行了切换,此时,程序的共享变量num是十0,中间变量tmp也是十0
在线程阻塞过后,将共享变量num的值减1,值变为9九此时其余的线程得到cpu的实行机会,而当前线程中的共享变量num的值依然100所以实行减1操作后,又将中间值赋值给共享变量num所以num的值一直为9九

  • 线程的实市场价格况
![](https://upload-images.jianshu.io/upload_images/6052465-461749d8c9eb7ea5.png)

多线程抢占.png

五 条件变量同步(Condition)

     
有1类线程须求满意条件之后本事够继续施行,Python提供了threading.Condition
对象用于规范变量线程的支撑,它除了能提供RubiconLock()或Lock()的诀窍外,还提供了
wait()、notify()、notifyAll()方法。

      lock_con=threading.Condition([Lock/Rlock]):
锁是可选选项,不传人锁,对象活动制造一个君越Lock()。

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

实例

亚洲必赢官网 38

亚洲必赢官网 39

import threading,time
from random import randint
class Producer(threading.Thread):
    def run(self):
        global L
        while True:
            val=randint(0,100)
            print('生产者',self.name,":Append"+str(val),L)
            if lock_con.acquire():
                L.append(val)
                lock_con.notify()
                lock_con.release()
            time.sleep(3)
class Consumer(threading.Thread):
    def run(self):
        global L
        while True:
                lock_con.acquire()
                if len(L)==0:
                    lock_con.wait()
                print('消费者',self.name,":Delete"+str(L[0]),L)
                del L[0]
                lock_con.release()
                time.sleep(0.25)

if __name__=="__main__":

    L=[]
    lock_con=threading.Condition()
    threads=[]
    for i in range(5):
        threads.append(Producer())
    threads.append(Consumer())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

亚洲必赢官网 40

Python 同步锁

操作锁的办法在threading 模块中 Lock()

  • threading.Lock() 会得到壹把锁
  • Python 中使用acquire() 获得锁

r = threading.Lock()
# 加锁
r.acquire()
  • Python中使用release()释放锁

r.release()

加锁后代码

'''
线程安全问题
'''
# 定义一个共享变量
import threading
import time
num = 100
r = threading.Lock()
def sub():
    # 操作类变量
    global num
    r.acquire()
    tmp = num
    time.sleep(0.1)
    num = tmp - 1
    r.release()
if __name__ == '__main__':
    thread_list = []
    for i in range(100):
        t1 = threading.Thread(target=sub)
        t1.start()
        thread_list.append(t1)
    for i in range(100):
        t2 = thread_list[i]
        t2.join()
print('final num' + str(num))

六 同步条件(伊芙nt)

     
条件同步和规格变量同步差不离意思,只是少了锁功用,因为条件同步设计于不访问共享能源的尺度环境。event=threading.伊夫nt():条件环境目的,先河值
为False;

亚洲必赢官网 41

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

event.wait():如果 event.isSet()==False将阻塞线程;

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

event.clear():恢复event的状态值为False。

亚洲必赢官网 42

实例1:

亚洲必赢官网 43

亚洲必赢官网 44

import threading,time
class Boss(threading.Thread):
    def run(self):
        print("BOSS:今晚大家都要加班到22:00。")
        event.isSet() or event.set()
        time.sleep(5)
        print("BOSS:<22:00>可以下班了。")
        event.isSet() or event.set()
class Worker(threading.Thread):
    def run(self):
        event.wait()
        print("Worker:哎……命苦啊!")
        time.sleep(0.25)
        event.clear()
        event.wait()
        print("Worker:OhYeah!")
if __name__=="__main__":
    event=threading.Event()
    threads=[]
    for i in range(5):
        threads.append(Worker())
    threads.append(Boss())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

亚洲必赢官网 45

实例2:

亚洲必赢官网 46

亚洲必赢官网 47

import threading,time
import random
def light():
    if not event.isSet():
        event.set() #wait就不阻塞 #绿灯状态
    count = 0
    while True:
        if count < 10:
            print('\033[42;1m--green light on---\033[0m')
        elif count <13:
            print('\033[43;1m--yellow light on---\033[0m')
        elif count <20:
            if event.isSet():
                event.clear()
            print('\033[41;1m--red light on---\033[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()

亚洲必赢官网 48

线程中的死锁和递归锁

在线程间共享八个能源的时候,假使三个线程分别侵吞1部分财富并且还要等待对方释放对方的财富,就会导致死锁,因为系统决断那某个能源正在使用,所以那多个线程在无外力成效下将直接等候下去
看个栗子:

'''
线程死锁
'''

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()  # 等待线程结束,后面再讲。

在以上程序中,多个线程互争执有对方的锁并且等待对方释放,这就产生了死锁

七 信号量(Semaphore)

     
实信号量用来决定线程并发数的,BoundedSemaphore或Semaphore管理1个放到的计数
器,每当调用acquire()时-1,调用release()时+一。

      计数器不可能小于0,当计数器为
0时,acquire()将卡住线程至三只锁定状态,直到别的线程调用release()。(类似于停车位的概念)

     
BoundedSemaphore与塞马phore的唯1不一样在于前者将要调用release()时检查计数
器的值是不是超过了计数器的上马值,要是抢先了将抛出二个不行。

实例:

亚洲必赢官网 49

亚洲必赢官网 50

import threading,time
class myThread(threading.Thread):
    def run(self):
        if semaphore.acquire():
            print(self.name)
            time.sleep(5)
            semaphore.release()
if __name__=="__main__":
    semaphore=threading.Semaphore(5)
    thrs=[]
    for i in range(100):
        thrs.append(myThread())
    for t in thrs:
        t.start()

亚洲必赢官网 51

缓解死锁的诀要

  • threading.本田UR-VLock() 可重入锁
    为了扶助在同一线程中再三呼吁同1能源,python提供了“可重入锁”:threading.奥迪Q7Lock。昂CoraLock内部维护着3个Lock和1个counter变量,counter记录了acquire的次数,从而使得能源得以被多次acquire。直到2个线程全部的acquire都被release,其余的线程工夫赢得财富。可重入锁的中间维持了三个计数器和锁对象。

 8 二十拾二线程利器(queue)

     queue is especially useful in threaded programming when information
must be exchanged safely between multiple threads.

信号量

功率信号量用来调整线程并发数的,BoundedSemaphore或Semaphore管理八个放权的计数器,每当调用acquire()时-一,调用release()时+一
计数器无法小于0当计数器为0时,acquire()将阻塞线程至3头锁定状态,直到其余线程调用release()。
BoundedSemaphore与塞马phore的绝无仅有分裂在于前者将要调用release()时检查计数器的值是还是不是超过了计数器的早先值。假使赶过了将抛出一个百般

queue列队类的法子

亚洲必赢官网 52

始建1个“队列”对象
import Queue
q = Queue.Queue(maxsize = 10)
Queue.Queue类正是1个系列的协同完毕。队列长度可为Infiniti也许轻易。可因此Queue的构造函数的可选参数maxsize来设定队列长度。假设maxsize小于一就象征队列长度Infiniti。

将3个值放入队列中
q.put(10)
调用队列对象的put()方法在队尾插入多少个类别。put()有多个参数,第三个item为供给的,为插入项指标值;第一个block为可选参数,默以为
一。假诺队列当前为空且block为一,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、还有1种是事先级队列等级越低越先出来。   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() 在产生1项职业之后,q.task_done()
函数向职责已经形成的体系发送贰个复信号
q.join() 实际上意味着等到队列为空,再进行别的操作

亚洲必赢官网 53

开创时限信号量

  • threading.BoundedSemaphore(num) 钦定连续信号量为num

import threading

import time


class Mythread(threading.Thread):
    def run(self):
        # 判断是否加锁
        if semaphore.acquire():
            print(self.name)
            time.sleep(1)
            # 释放锁
            semaphore.release()


if __name__ == '__main__':
    # 创建带有信号量的锁
    semaphore = threading.BoundedSemaphore(5)
    # 存放线程的序列
    thrs = []
    for i in range(100):
        thrs.append(Mythread())
    for t in thrs:
        t.start()

实例

实例1:

亚洲必赢官网 54

亚洲必赢官网 55

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()

亚洲必赢官网 56

实例2:

亚洲必赢官网 57

亚洲必赢官网 58

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('\033[32;1mConsumer %s has eat %s baozi...\033[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()

亚洲必赢官网 59

实例3:

亚洲必赢官网 60

亚洲必赢官网 61

#实现一个线程不断生成一个随机数到一个队列中(考虑使用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()

亚洲必赢官网 62

在意:列表是线程不安全的

亚洲必赢官网 63

亚洲必赢官网 64

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()

亚洲必赢官网 65

原则变量同步

有1类线程要求满足条件之后技艺够继续实行,Python提供了threading.Condition
对象用于规范变量线程的支撑,它除了能提供LacrosseLock()或Lock()的法子外,还提供了
wait()、notify()、notifyAll()方法。
基准变量也是线程中的一把锁,不过规格变量能够完结线程间的通讯,类似于Java中的唤醒和等候

九 Python中的上下文物管理理器(contextlib模块)

上下文物管理理器的任务是:代码块执行前准备,代码块实行后处置

创办标准变量锁

  • lock_con = threading.Condition(Lock/奥迪Q伍lock)
    锁是可选选项,不扩散锁对象活动成立贰个LX570Lock()
  • wait() 条件不满意时调用,线程会放出锁并跻身等待绿灯
  • notify() 条件创制后调用,通告等待池激活1个线程
  • notifyAll() 条件创建后调用,文告等待池激活全部线程
    看个栗子

'''
线程条件变量
'''
import threading
from random import randint

import time


class Producer(threading.Thread):
    def run(self):
        global L
        while True:
            val = randint(0, 100)
            print('生产者', self.name, ':Append' + str(val), L)
            if lock_con.acquire():
                L.append(val)
                lock_con.notify()
                lock_con.release()
            time.sleep(3)


class Consumer(threading.Thread):
    def run(self):
        global L
        while True:
            lock_con.acquire()
            if len(L) == 0:
                lock_con.wait()
            print('消费者',self.name,"Delete"+str(L[0]),L)
            del  L[0]
            lock_con.release()
            time.sleep(0.25)


if __name__ == '__main__':
    L = []
    # 创建条件变量锁
    lock_con = threading.Condition()
    # 线程存放列表
    threads = []
    for i in range(5):
        threads.append(Producer())
    threads.append(Consumer())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

一、如何利用上下文管理器:

怎么着开垦一个文书,并写入”hello world”

1
2
3
4
5
filename="my.txt"
mode="w"
f=open(filename,mode)
f.write("hello world")
f.close()

当发生非常时(如磁盘写满),就一向不机会实行第陆行。当然,大家能够动用try-finally语句块举行打包:

1
2
3
4
5
writer=open(filename,mode)
try:
    writer.write("hello world")
finally:
    writer.close()

当大家进行复杂的操作时,try-finally语句就会变得丑陋,选择with语句芒写:

1
2
with open(filename,mode) as writer:
    writer.write("hello world")

as指代了从open()函数重回的内容,并把它赋给了新值。with完毕了try-finally的天职。

3只条件event

条件同步和标准化变量同步大致意思,只是少了锁功用,因为条件同步设计于不访问共享财富的规则环境。event=threading.伊芙nt():条件环境指标,初叶值
为False;

  • event.isSet():再次来到event的景况值;

  • event.wait():固然 event.isSet()==False将封堵线程;

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

  • event.clear():复苏event的图景值为False。
    举个栗子:

'''
同步条件event
'''
import threading

import time


class Boss(threading.Thread):
    def run(self):
        print('BOSS: 今晚加班')
        # 改变事件
        event.isSet() or event.set()
        time.sleep(5)
        print('BOSS:加班结束')
        event.isSet() or event.set()


class Worker(threading.Thread):
    def run(self):
        event.wait()
        print('WORKER:OH NO')
        time.sleep(0.25)
        # 改变同步事件标志
        event.clear()
        event.wait()
        print('WORKER:OH YEAD!')

if __name__ == '__main__':
    # 获取同步事件
    event = threading.Event()
    threads = []
    for i in range(5):
        threads.append(Worker())
    threads.append(Boss())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

二、自定义上下文物管理理器  

with语句的法力类似于try-finally,提供一种上下文机制。要使用with语句的类,在那之中间必须提供三个放置函数__enter__和__exit__。前者在主体代码执行前进行,后者在关键性代码实行后实行。as前面的变量,是在__enter__函数中回到的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class echo():
    def output(self):
        print "hello world"
    def __enter__(self):
        print "enter"
        return self  #可以返回任何希望返回的东西
    def __exit__(self,exception_type,value,trackback):
        print "exit"
        if exception_type==ValueError:
            return True
        else:
            return Flase
  
>>>with echo as e:
    e.output()
     
输出:
enter
hello world
exit

完备的__exit__函数如下:

1
def __exit__(self,exc_type,exc_value,exc_tb)

其中,exc_type:相当类型;exc_value:异常值;exc_tb:万分追踪消息

当__exit__回来True时,至极不传播

线程利器队列 queue

队列是1种数据结构,队列分为先进先出(FIFO) 和 先进后出(FILO)
Python Queue模块有三种队列及构造函数:
一、Python Queue模块的FIFO队列先进先出。 class queue.Queue(maxsize)
二、LIFO类似于堆,即先进后出。 class queue.LifoQueue(maxsize)
三、还有1种是事先级队列等级越低越先出来。 class
queue.PriorityQueue(maxsize)
队列能够保证数据安全,是因为队列的中间维护着1把锁。各种去队列中取数据的都会保证数据的安全。而列表纵然有所同样的遵从,但是列表不是数码安全的

3、contextlib模块  

contextlib模块的意义是提供更易用的上下文物管理理器,它是透过Generator落成的。contextlib中的contextmanager作为装饰器来提供壹种针对函数品级的上下文物管理理机制,常用框架如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from contextlib import contextmanager
@contextmanager
def make_context():
    print 'enter'
    try:
        yield "ok"
    except RuntimeError,err:
        print 'error',err
    finally:
        print 'exit'
         
>>>with make_context() as value:
    print value
     
输出为:
    enter
    ok
    exit

内部,yield写入try-finally中是为着保险丰硕安全(能处理卓殊)as后的变量的值是由yield重临。yield前面包车型大巴语句可看作代码块实施前操作,yield之后的操作可以当作在__exit__函数中的操作。

以线程锁为例:

亚洲必赢官网 66

@contextlib.contextmanager
def loudLock():
    print 'Locking'
    lock.acquire()
    yield
    print 'Releasing'
    lock.release()

with loudLock():
    print 'Lock is locked: %s' % lock.locked()
    print 'Doing something that needs locking'

#Output:
#Locking
#Lock is locked: True
#Doing something that needs locking
#Releasing

亚洲必赢官网 67

开创七个队列

Queue.Queue类便是一个队列的一齐达成。队列长度可为Infiniti大概个别。可经过Queue的构造函数的可选参数maxsize来设定队列长度。尽管maxsize小于1就意味着队列长度Infiniti。

4、contextlib.nested:减少嵌套

对于:

1
2
3
with open(filename,mode) as reader:
    with open(filename1,mode1) as writer:
        writer.write(reader.read())

能够透过contextlib.nested进行简化:

1
2
with contextlib.nested(open(filename,mode),open(filename1,mode1)) as (reader,writer):
    writer.write(reader.read())

在python 二.7及其后,被1种新的语法取代:

1
2
with open(filename,mode) as reader,open(filename1,mode1) as writer:
    writer.write(reader.read())

向队列中插入数据

  • q.put(item,block)
    调用队列对象的put()方法在队尾插入二个种类。put()有五个参数,第2个item为需要的,为插入项目标值;第二个block为可选参数,默以为一。如果队列当前为空且block为一,put()方法就使调用线程暂停,直到空出1个多少单元。要是block为0,put方法将引发Full卓殊。

5、contextlib.closing() 

file类直接帮忙上下文物管理理器API,但多少代表展开句柄的对象并不帮衬,如urllib.urlopen()重返的目的。还有个别遗留类,使用close()方法而不扶助上下文管理器API。为了保障关闭句柄,须求动用closing()为它创造三个上下文物管理理器(调用类的close方法)。

亚洲必赢官网 68

亚洲必赢官网 69

import contextlib
class myclass():
    def __init__(self):
        print '__init__'
    def close(self):
        print 'close()'

with contextlib.closing(myclass()):
    print 'ok'

输出:
__init__
ok
close()

亚洲必赢官网 70

从队列中抽取数据

  • q.get()
    调用队列对象的get()方法从队头删除并回到二个门类。可选参数为block,默许为True。假设队列为空且block为True,get()就使调用线程暂停,直至有品种可用。要是队列为空且block为False,队列将引发Empty十分。

十 自定义线程池

简短版本:

亚洲必赢官网 71

亚洲必赢官网 72

import queue
import threading
import time

class ThreadPool(object):

    def __init__(self, max_num=20):
        self.queue = queue.Queue(max_num)
        for i in range(max_num):
            self.queue.put(threading.Thread)

    def get_thread(self):
        return self.queue.get()

    def add_thread(self):
        self.queue.put(threading.Thread)


'''
pool = ThreadPool(10)

def func(arg, p):
    print(arg)
    time.sleep(1)
    p.add_thread()


for i in range(30):
    Pool = pool.get_thread()
    t = Pool(target=func, args=(i, pool))
    t.start()
'''

亚洲必赢官网 73

复杂版本:

亚洲必赢官网 74

亚洲必赢官网 75

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import queue
import threading
import contextlib
import time

StopEvent = object()

class ThreadPool(object):

    def __init__(self, max_num, max_task_num = None):
        if max_task_num:
            self.q = queue.Queue(max_task_num)
        else:
            self.q = queue.Queue()
        self.max_num = max_num
        self.cancel = False
        self.terminal = False
        self.generate_list = []
        self.free_list = []

    def run(self, func, args, callback=None):
        """
        线程池执行一个任务
        :param func: 任务函数
        :param args: 任务函数所需参数
        :param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数)
        :return: 如果线程池已经终止,则返回True否则None
        """
        if self.cancel:
            return
        if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
            self.generate_thread()
        w = (func, args, callback,)#主线程
        self.q.put(w)#主线程

    def generate_thread(self):
        """
        创建一个线程
        """
        t = threading.Thread(target=self.call)
        t.start()

    def call(self):
        """
        循环去获取任务函数并执行任务函数
        """
        current_thread = threading.currentThread()
        self.generate_list.append(current_thread)

        event = self.q.get()#if q为空,则阻塞住,一直等到有任务进来并把它取出来
        while event != StopEvent:

            func, arguments, callback = event
            try:
                result = func(*arguments)
                success = True
            except Exception as e:
                success = False
                result = None

            if callback is not None:
                try:
                    callback(success, result)
                except Exception as e:
                    pass

            with self.worker_state(self.free_list, current_thread):
                if self.terminal:
                    event = StopEvent
                else:
                    event = self.q.get()#key:该线程在这里继续等待新的任务,任务来了,继续执行
                                        #暂时将该线程对象放到free_list中。
        else:

            self.generate_list.remove(current_thread)

    def close(self):
        """
        执行完所有的任务后,所有线程停止
        """
        self.cancel = True
        full_size = len(self.generate_list)
        while full_size:
            self.q.put(StopEvent)
            full_size -= 1

    def terminate(self):
        """
        无论是否还有任务,终止线程
        """
        self.terminal = True

        while self.generate_list:
            self.q.put(StopEvent)

        self.q.queue.clear()

    @contextlib.contextmanager
    def worker_state(self, free_list, worker_thread):
        """
        用于记录线程中正在等待的线程数
        """
        free_list.append(worker_thread)#新的任务来的时候判断
                                 # if len(self.free_list) == 0 and len(self.generate_list) < self.max_num
                                 # 任务得创建新的线程来处理;如果len(self.free_list) != 0:由阻塞着的存在free_list中的线程处理(event = self.q.get())
        try:
            yield
        finally:
            free_list.remove(worker_thread)

# How to use


pool = ThreadPool(5)

def callback(status, result):
    # status, execute action status
    # result, execute action return value
    pass


def action(i):
    time.sleep(1)
    print(i)

for i in range(30):
    ret = pool.run(action, (i,), callback)

time.sleep(2)
print(len(pool.generate_list), len(pool.free_list))
print(len(pool.generate_list), len(pool.free_list))

# pool.close()
# pool.terminate()

亚洲必赢官网 76

 延伸:

亚洲必赢官网 77

亚洲必赢官网 78

import contextlib
import socket
@contextlib.contextmanager
def context_socket(host,port):
    sk=socket.socket()
    sk.bind((host,port))
    sk.listen(5)
    try:
        yield sk
    finally:sk.close()

with context_socket('127.0.0.1',8888) as socket:
    print(socket)

亚洲必赢官网 79

 

API

  • 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() 在成功1项职业未来,q.task_done()
    函数向任务已经完毕的行列发送四个非非确定性信号
  • q.join() 实际上意味着等到队列为空,再实践其余操作
网站地图xml地图