我有一组耗时的操作,这些操作专门针对我的应用的每个用户,它全部封装在一个方法中(例如write_collections
方法)。在这个方法中,程序与Facebook和MongoDB进行通信。我想在每个用户的线程中运行此方法。
在get '/'
Sinatra路由中调用此线程,但仅在get '/calculate'
上需要线程的结果(数据库中的状态)。我的想法是在get '/'
上运行该线程并在get '/calculate'
上加入它,以确保在计算用户开始的结果之前,所有用户的数据都已在数据库中正确写入。
举例说明:
get "/" do
@user = @graph.get_object("me")
data_thread = Thread.new do
write_collections(@user)
end
session[:data_thread] = data_thread.object_id
erb :index
end
get "/calculate" do
begin
# Is this safe enough?
if ObjectSpace._id2ref(session[:data_thread]).alive?
data_thread = ObjectSpace._id2ref(session[:data_thread])
data_thread.join
end
rescue RangeError => range_err
# session[:data_thread] is not id value
# direct access to /calculate without session
rescue TypeError => type_err
# session[:data_thread] is nil
end
# do calculations based on state in database
# and show results to user
"<p>Under construction</p>"
end
要找到特定用户应该等待加入的正确线程,我目前使用ObjectSpace._id2ref(session[:data_thread])
。
来自Object#object_id
的官方Ruby文档:
object_id→fixnum: 返回obj的整数标识符。对于给定对象的所有id调用都将返回相同的数字,并且没有两个活动对象将共享id。
和ObjectSpace
:
ObjectSpace模块包含许多与垃圾收集工具交互的例程,允许您使用迭代器遍历所有生存对象。
让我们假设以下情况:
'/'
[现在 A 主题以object_id
a 启动object_id
] '/'
[现在 B 主题使用相同的object_id
a (* < em>有可能吗?)] '/calculate'
[session[:data_thread]
a ,因此ObjectSpace._id2ref(session[:data_thread])
实际上是 B 主题] 状态不一致 - 用户 A 正在等待主题 B 。
configure do
# map user_id to corresponding user's thread
data_threads_hash = {}
set :data_threads_hash, data_threads_hash
end
get "/" do
@user = @graph.get_object("me")
data_thread = Thread.new do
write_collections(@user)
end
session[:user_id] = @user['id']
settings.data_threads_hash[session[:user_id]] = data_thread
erb :index
end
get "/calculate" do
if settings.data_threads_hash[session[:user_id]].alive?
data_thread = settings.data_threads_hash[session[:user_id]]
data_thread.join
settings.data_threads_hash.delete session[:user_id]
end
# do calculations based on state in database
# and show results to user
"<p>Under construction</p>"
end
我在阅读Sinatra: README后试过这个。 在配置:
下在启动时,在任何环境中运行一次 ... 您可以通过设置访问这些选项 ...
在范围和绑定下,应用程序/类范围:
每个Sinatra应用程序都对应于Sinatra :: Base的子类。如果您使用顶级DSL(需要'sinatra'),那么这个类是Sinatra :: Application,否则它是您明确创建的子类。在类级别,您有get或之前的方法,但是您无法访问请求或会话对象,因为所有请求只有一个应用程序类。
我正在使用顶级DSL。
通过set创建的选项是类级别的方法...您可以访问 范围对象(类)如下:来自内部的设置 请求范围
欢迎任何评论或参考。
答案 0 :(得分:2)
我不确定这种设计是否有意义。为什么每个用户都需要自己的线程?为什么一个请求会加入另一个请求创建的线程?即使有可能(仅使用一个dyno),我认为这不是一个做你想做的事情的好方法。
根据问题中应用的说明,您希望在calculate
方法完成后运行一些write_collections
方法。那么,为什么write_collections
方法不能调用某些calculate
方法呢?或者,为什么不能使用后置滤波器或观察器进行计算?
更一般地说,你似乎混淆了两个独立的功能:
calculate
功能GET /calculate
我认为应该只有一个触发器来调用calculate
功能。在用户请求write_collections
1>}时,它可以在GET /calculate
或完成时完成。
更通用的解决方案是在后台运行calculate
功能,并将结果保存到数据库中。稍后,当用户发出请求时,它已准备就绪,可以快速返回。