多线程--Event

Event 简介

Event 事件 是线程间通信的最简单方法之一,主要用于线程同步。

 

处理机制

定义一个全局内置标志Flag,如果Flag为False,执行到 event.wait 时程序就会阻塞,如果Flag为True,event.wait 便不会阻塞

【注意如果处于阻塞状态,不管在哪使得Flag为true,wait都会继续执行】

 

接口

set()     将标志设置为True,并通知所有处于阻塞状态的线程恢复运行

clear()       将标志设置为False

isSet()  获取内置标志的状态,返回 True 或者 False

wait(timeout)   如果标志为False,将使得线程阻塞,如果为True,继续运行,默认为False

 

示例代码

示例代码--等通知

import threading
import time

event = threading.Event()


def chihuoguo(name):
  # 等待事件,进入等待阻塞状态
  print '%s 已经启动' % threading.currentThread().getName()
  print '小伙伴 %s 已经进入就餐状态!'%name
  time.sleep(1)
  event.wait()
  # 收到事件后进入运行状态
  print '%s 收到通知了.' % threading.currentThread().getName()
  print '小伙伴 %s 开始吃咯!'%name


threads = []
thread1 = threading.Thread(target=chihuoguo, args=("a", ))
thread2 = threading.Thread(target=chihuoguo, args=("b", ))
threads.append(thread1)
threads.append(thread2)

for thread in threads:
  thread.start()

time.sleep(0.1)
# 发送事件通知
print '主线程通知小伙伴开吃咯!'
event.set()

 

示例代码--互相通知

import threading
import time

def producer():
    print u'等人来买包子....'
    event.wait()
    #event.clear()
    print event.isSet()
    print u'chef:sb is coming for baozi...'
    print u'chef:making a baozi for sb...'
    time.sleep(5)

    print u'chef:你的包子好了...'
    event.set()

def consumer():
    print u'chenchao:去买包子....'
    event.set()

    time.sleep(2)
    print 'chenchao:waiting for baozi to be ready...'
    print event.wait()
    print u'chenchao:哎呀真好吃....'

event = threading.Event()

p = threading.Thread(target=producer,args=())
c = threading.Thread(target=consumer,args=())
p.start()
c.start()

输出

等人来买包子....
chenchao:去买包子....
True
chef:sb is coming for baozi...
chef:making a baozi for sb...
chenchao:waiting for baozi to be ready...
True
chenchao:哎呀真好吃....
chef:你的包子好了...

上面实现了一个生产者-消费者模式,显然有错误,包子还没做好就吃上了。

稍微细心的缕下思路就会发现,消费者中的wait并没有阻塞线程,因为Flag此时为True

解决方法:

1. 用另一个 event2 来阻塞线程

2. 在生产者获得set时及时把Flag设置为False 【取消生产者中 event.clear() 的注释即可】

 

注意点1 

import threading
event = threading.Event()
print(1)
print(event.wait())     # 打印也会使线程阻塞
print(2)

 

注意点2

import time
import threading

def myfunc():
    while 1:
        time.sleep(1)
        print(1)

event = threading.Event()

ts = []
if len(ts) > 2:
    event.wait()        # 此时阻塞,已经开启的线程将继续运行

for i in range(12):
    t = threading.Thread(target=myfunc)
    t.start()
    ts.append(t)

 

实战案例

多线程验证代理ip的有效性

 

问题:计算机并不能无休止的增加线程,每台计算机都有自己的上限

### 计算机能够执行的最大线程数

def myfunc():
    time.sleep(2)

count = 0
while 1:
    count += 1
    print(count)
    t = threading.Thread(target=myfunc)
    t.start()

超过上限,就会报错

thread.error: can't start new thread

 

思路:设置最大线程数,当启动的线程超过最大限制时,阻塞,不再生成新线程,并且持续跟踪线程数,一旦减小或者小于某个阈值,就取消阻塞,继续生成线程

class MyTestProxy(object):
    def __init__(self):
        self.sFile = 'ip.txt'
        self.dFile = 'alive.txt'
        self.url = 'https://www.qiushibaike.com/text/'
        self.threadmax = 500    # 最大线程数
        self.threadmin = 400    # 最低线程数
        self.timeout = 3
        self.regex = re.compile('qiushibaike.com')
        self.aliveList = []

        self.event = threading.Event()
        self.event2 = threading.Event()
        self.lock = threading.Lock()

        self.run()

    def run(self):
        with open(self.sFile, 'rb') as fp:
            lines = fp.readlines()
            self.ts = 0         # 初始化线程数
            while lines:
                if self.ts > self.threadmax:
                    self.event.clear()
                    self.event.wait()           # 超过设定线程就阻塞

                line = lines.pop()
                t = threading.Thread(target=self.linkWithProxy, args=(line, ))
                t.start()
                self.lock.acquire()
                self.ts += 1    # 启动一个就加1,ts 被其他线程一直在更新,所以加锁
                self.lock.release()

            self.event2.wait()  # 处理完毕后统一存储
            with open(self.dFile, 'w') as fp:
                for i in range(len(self.aliveList)):
                    fp.write(self.aliveList[i])

    def act(self):
        # 执行完一个线程就减1,因为同时执行,要加锁
        self.lock.acquire()
        self.ts -= 1
        self.lock.release()
        print(self.ts)
        if self.ts < self.threadmin:
            self.event.set()        # 小于最低线程取消阻塞
        if self.ts == 0:
            self.event2.set()

    def linkWithProxy(self, line):
        # 爬虫
        server = line.strip()
        # print(server)
        protocol = line.split(':')[0]
        opener = urllib2.build_opener(urllib2.ProxyHandler({protocol:server}))
        urllib2.install_opener(opener)

        try:
            response = urllib2.urlopen(self.url, timeout=self.timeout)
        except:
            return
        else:
            try:
                str = response.read()
                if self.regex.search(str):
                    # print(str)
                    print('%s connect success'%server)
                    print(response.geturl())
                    self.aliveList.append(line)
            except:
                return
        finally:
            self.act()


if __name__ == '__main__':
    time.clock()
    tp = MyTestProxy()
    print(time.clock())

 

效率还是不错的

 

 

参考资料:

http://www.cnblogs.com/huxi/archive/2010/06/26/1765808.html

转载于:https://www.cnblogs.com/yanshw/p/10863479.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值