python:多线程使用生成器提示ValueError: generator already executing

写了个爬取ROE的爬虫,出现了一个问题,对迭代器对象使用threading的时候报错:generator already executing
搜了一下百度上只有两个方法,方法相同,但是代码一个有问题,现在取正确的方法记录如下:

最近写脚本时涉及到了多线程和生成器,在使用过程中遇到了数据竞争的问题,特此记录下。
最终代码如下:

import threading

class threadsafe_generator:
    
    def __init__(self, gen):
        self.gen = gen
        self.lock = threading.Lock()
    
    def __iter__(self):
        return self
    
    def __next__(self):
        with self.lock:
            return self.gen.__next__()
            
def main(code):
    for i in code:
        if i[1]:
            x = get_roe(i[1])
            print(i[0])
            save(x, i[1], i[2])
        else:
            continue
        
if __name__ == '__main__':    
    code = threadsafe_generator(get_code()) # 生成新的迭代器
    years = int(input('请输入年数:'))
    roes = float(input('请输入roe:'))
    tasks = []
    for t in range(10):
        t = threading.Thread(target=main, args=(code,))
        t.start()
        tasks.append(t)
    for task in tasks:
        task.join()

在执行脚本时提示以下错误:ValueError: generator already executing

多线程同时请求生成器数据(即同时调用next方法)就会引发该错误,所以只要在调用next方法时加个锁就可以解决。

在脚本开头处加入:

class threadsafe_iter:
    """Takes an iterator/generator and makes it thread-safe by
    serializing call to the `next` method of given iterator/generator.
    """

    def __init__(self, it):
        self.it = it
        self.lock = threading.Lock()

    def __iter__(self):
        return self

    def __next__(self): # python3
        with self.lock:
            return self.it.__next__()
  
     # def next(self): # python2
     #     with self.lock:
     #       return self.it.next()



def threadsafe_generator(f):
    """A decorator that takes a generator function and makes it thread-safe.
    """

    def g(*a, **kw):
        return threadsafe_iter(f(*a, **kw))

    return g

说明:

由于next方法的不同,所以需要区别python环境。
threadsafe_generator为装饰器,使用时只需在生成器上加入@threadsafe_generator即可。
具体的例子可以参考:https://gist.github.com/platdrag/e755f3947552804c42633a99ffd325d4

以上为简书的内容,解决方法是创建一个类,但没写后续用法。

结合另外一个帖子的内容,得到最终正确用法

import threading  
  
class threadsafe_generator:  
    """Takes an generator and makes it thread-safe by 
    serializing call to the `next` method of given generator. 
    """  
    def __init__(self, gen):  
        self.gen = gen  
        self.lock = threading.Lock()  
  
    def __iter__(self):  
        return self.next()  
  
    def next(self):  
        with self.lock:  
            return self.gen.next()  
  
  
# 定义我们的生成器  
def all_keywords():  
    for row in csv.reader(open('companies.csv')):  
        if row[0]:  
           yield row[0]  
  
  
# 将其转换为线程安全的  
keywords = threadsafe_generator(all_keywords())  
# 然后在线程中就可以随意地使用keywords.next()而不必担心"generator already executing"异常了。  

最后的方法就是用新类创建一个新的迭代器对象,后边这种方法的代码是错误的,第一种方法正确,但是没有用法,需要两种结合一下

参考链接:https://www.jianshu.com/p/79dcfd77e56c
参考链接:http://www.redicecn.com/html/Python/20120619/417.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值