我有一组针对我的应用程序的每个单独用户具体操作,这全部包含在一个方法中(例如write_collections
方法)。在此方法中,程序与Facebook和MongoDB通信。我想为每个用户在线程中运行此方法。
这个线程被称为get '/'
Sinatra路由,但是线程结果(数据库中的状态)只需要在get '/calculate'
上使用。我的想法是在get '/'
上运行线程,在get '/calculate'
上加入它以确保在开始计算用户结果之前所有用户数据都已正确写入数据库。
举个例子:
方法 I
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的A线程] - 线程A已完成[它不再处于活动状态,其
object_id
已被释放] - 用户B访问
'/'
[现在启动了具有相同object_id
a的B线程(*可能吗?)] - 用户A访问
'/calculate'
[session[:data_thread]
是a,因此ObjectSpace._id2ref(session[:data_thread])
实际上是B线程。] 不一致状态-用户A正在等待线程B。
- 在Sinatra、Thin、Heroku中是否可能出现这种情况?
方法二
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后,我尝试了这个方法。在配置(Configuration)中:
在任何环境下启动时运行一次 ... 您可以通过settings访问这些选项 ...
在范围和绑定(Scopes and Binding)下,应用程序/类作用域(Application/Class Scope):
每个Sinatra应用对应于Sinatra::Base的子类。如果您使用顶级DSL(require 'sinatra'),则该类为Sinatra::Application,否则它是您显式创建的子类。在类级别上,您有像get或before这样的方法,但您无法访问请求或会话对象,因为所有请求都只有一个应用程序类。
我正在使用顶级DSL。
通过set创建的选项是类级别的方法...您可以通过request scope中的settings访问范围对象(即类)
- @FrederickCheung在评论中提到的内容以及从Scopes和Binding中引用的内容,请记住,如果我需要更多的dyno/worker(目前此应用程序仅使用一个dyno),那么这种方法是否有效?
总结
- 在Sinatra中,我应该如何处理上述情况中的用户及其对应的线程,以上示例中的方法好坏如何?
欢迎任何评论或参考。