我正在使用带有python的app引擎
我每天都要对值列表执行任务。这些值存储在targets属性下的“项目”值中:
myproject1.targets=['foo','bar','foo2','bar2','foo3','bar3','foo4','bar4','foo5','bar5']
我的目标是为每个值排队调用url:url_to_my_worker
,并将值作为参数。
我目前在我的数据库中只有一个项目对象。
我运行schedule_daily_projects_tasks,它基本上为每个配置文件对象排队schedule_daily_profile_tasks
class schedule_daily_projects_tasks(webapp.RequestHandler):
def post(self):
key=self.request.get('key')
pro=project.get(key)
profiles=my_profile.gql("WHERE project=:1",pro)
logging.info(profiles)
for profile in profiles:
taskqueue.add(url='/control/schedule_daily_profile_tasks', params={'key': profile.key()})
然后为每个配置文件运行'schedule_daily_profile_tasks'。
class schedule_daily_profile_tasks(webapp.RequestHandler):
def post(self):
key=self.request.get('key')
profile=my_profile.get(key)
pro=profile.project
for i in range(1, 6):
now=datetime.now()
tim=datetime(year=now.year, month=now.month, day=now.day, hour=8+i)
screen_name=pro.targets.pop()
taskqueue.add(url='/url_to_my_worker', params={'profk': key, 'screen_name':screen_name}, eta=tim)
pro.put()
假设我的数据库中有5个配置文件对象:profile1到profile5 因此,如果一切顺利,我应该将5个任务排入网址'/ url_to_my_worker',并带有参数:
1) params={'profk': profile1.key(), 'screen_name':'bar5'}
2) params={'profk': profile2.key(), 'screen_name':'foo5'}
3) params={'profk': profile3.key(), 'screen_name':'bar4'}
4) params={'profk': profile4.key(), 'screen_name':'foo4'}
5) params={'profk': profile5.key(), 'screen_name':'bar3'}
但相反,我得到了:
1) params={'profk': profile1.key(), 'screen_name':'bar5'}
2) params={'profk': profile2.key(), 'screen_name':'bar5'}
3) params={'profk': profile3.key(), 'screen_name':'bar5'}
4) params={'profk': profile4.key(), 'screen_name':'bar5'}
5) params={'profk': profile5.key(), 'screen_name':'bar5'}
我认为任务运行得太快,所以n°2在n°1“弹出”之前开始。因此myproject1.targets具有相同的值。
如何确保列表中的每个值只使用一次?
非常感谢
答案 0 :(得分:2)
您遇到的问题或多或少与您描述的一样:您已将多个尝试同时修改同一数据存储对象的任务排入队列。由于您没有使用事务,因此多个任务最终会检索相同的数据,执行相同的操作,然后覆盖彼此的结果。
您可以使用数据存储区事务来避免这种情况,但更好的解决方案是重构您的任务,以便只有一个任务正在修改每个数据存储区实体。这样,您就不必担心同步或事务问题。
答案 1 :(得分:1)
您可能需要考虑将您的任务批量添加:
targets=pro.targets
tasks=[]
...
screen_name=targets.pop()
tasks.append(taskqueue.Task(url='/url_to_my_worker', params={'profk': key, 'screen_name':screen_name}, eta=tim))
...
pro.put()
taskqueue.Queue().add(tasks)
请注意,您可以通过创建将max_concurrent_requests指定为1的queue.yaml来避免特定队列上的任务同时运行:
queue:
- name: default
max_concurrent_requests: 1
答案 2 :(得分:0)
Python附带了一个内置的线程安全的队列库模块。
http://docs.python.org/library/queue.html
Queue模块实现多生产者,多消费者队列。当必须在多个线程之间安全地交换信息时,它在线程编程中特别有用。此模块中的Queue类实现了所有必需的锁定语义。这取决于Python中线程支持的可用性;参见线程模块。
你可以这样做,以便在调用pop()时,它只会阻塞,直到释放任何锁。
编辑:链接的示例页面使用队列模块完成其工作。
import Queue
targets = ['foo','bar','foo2','bar2','foo3','bar3','foo4','bar4','foo5','bar5']
queue = Queue.LifoQueue() # Last in first out
for target in targets:
queue.put(target)
myproject1.targets = queue
##########################
class schedule_daily_profile_tasks(webapp.RequestHandler):
....
screen_name=pro.targets.get(block=true)
答案 3 :(得分:0)
如果项目recs中没有大量的配置文件元素:Schedule_daily_projects_tasks()可以将所有配置文件元素连接到序列化字符串中。将该字符串作为参数传递给任务队列。每个任务进程从字符串中弹出一个元素,对其进行处理,如果没有异常发生,则使用较短的序列化字符串将任务排入队列。如果您正在对同一实体进行更新,请将任务排入大约两秒钟的时间延迟。这也有助于规范为处理任务队列而启动的实例数量,从而降低成本。