在 Rails 6.x 應用程式中,我有一個控制器方法,它處理超過 2 分鐘的查詢(以避免瀏覽器超時),建議用戶,存盤結果,并發送一個可以檢索它們以生成實時的鏈接頁面(帶有 Highcharts 圖表)。這作業正常。
現在,我正在嘗試使用一種方法來實作相同的邏輯,該方法通過 Tempfile 為報告的創建提供背景,如果查詢運行時間過長,則將內容附加到電子郵件中。如果未達到 2 分鐘超時,則此代碼可以正常作業,但如果達到超時,則 Tempfile 在注釋行處為空。
我嘗試將第二部分包裝在另一個執行緒中,并用互斥鎖包裝每個執行緒的內部,但這一切都超出了我的想象。我還沒有做過很多多執行緒,每次我做的時候,我都覺得自己跌跌撞撞,直到我得到它。這一次,我什至似乎無法跌入其中。
我不知道問題是出在我的執行緒上,還是出在 Tempfile 物件的競爭條件上。我以前在使用 Tempfiles 時遇到過麻煩,因為它們消失的速度似乎比我關閉它們的速度更快。這個是在寄出前清理干凈的嗎?檔案句柄實際上仍然存在于注釋點的檔案系統上,即使它是空的,所以我不清楚發生了什么。
def report
queue = Queue.new
file = Tempfile.new('report')
thr = Thread.new do
query = %Q(blah blah blah)
@calibrations = ActiveRecord::Base.connection.exec_query query
query = %Q(blah blah blah)
@tunings = ActiveRecord::Base.connection.exec_query query
if queue.empty?
unless @tunings.empty?
CSV.open(file.path, 'wb') do |csv|
csv << ["headers...", @parameters].flatten
@calibrations.each do |c|
line = [c["h1"], c["h2"], c["h3"], c["h4"], c["h5"], c["h6"], c["h7"], c["h8"]]
t = @tunings.select { |t| t["code"] == c["code"] }.first
@parameters.each do |parameter|
line << t[parameter.downcase]
end
csv << line
end
end
send_data file.read, :type => 'text/csv; charset=iso-8859-1; header=present', :disposition => "attachment; filename=\"report.csv\""
end
else
# When "timed out", `file` is empty here
NotificationMailer.report_ready(current_user, file.read).deliver_later
end
end
give_up_at = Time.now 120.seconds
while Time.now < give_up_at do
if !thr.alive?
break
end
sleep 1
end
if thr.alive?
queue << "Timeout"
render html: "Your report is taking longer than 2 minutes to generate. To avoid a browser timeout, it will finish in the background, and the report will be sent to you in email."
end
end
uj5u.com熱心網友回復:
檔案為空的原因是您給查詢 120 秒的時間來完成。如果 120 秒后還沒有發生,則將“超時”添加到佇列中。查詢仍在執行緒內運行,尚未到達檢查佇列是否為空的地步。當查詢完成時,由于佇列現在不是空的,因此您跳過撰寫 csv 檔案的部分并轉到該Notification.report行。此時檔案仍然是空的,因為您從未向其中寫入任何內容。
最后,我認為您需要重新考慮您要完成的作業的整體邏輯,并且需要在執行緒和頂層之間進行更多的通信。
每個執行緒都需要告訴頂層它是否已經發送了結果,而頂層需要讓執行緒知道它過去的時間直接發送結果,而不是應該通過電子郵件發送結果。
下面是一些我認為/希望能深入了解如何解決這個問題的代碼。
timeout_limit = 10
query_times = [5, 15, 1, 15]
timeout = []
sent_response = []
send_via_email = []
puts "time out is set to #{timeout_limit} seconds"
query_times.each_with_index do |query_time, query_id|
puts "starting query #{query_id} that will take #{query_time} seconds"
timeout[query_id] = false
sent_response[query_id] = false
send_via_email[query_id] = false
Thread.new do
## do query
sleep query_time
unless timeout[query_id]
puts "query #{query_id} has completed, displaying results now"
sent_response[query_id] = true
else
puts "query #{query_id} has completed, emailing result now"
send_via_email[query_id] = true
end
end
give_up_at = Time.now timeout_limit
while Time.now < give_up_at
break if sent_response[query_id]
sleep 1
end
unless sent_response[query_id]
puts "query #{query_id} timed out, we will email the result of your query when it is completed"
timeout[query_id] = true
end
end
# simulate server environment
loop { }
=>
time out is set to 10 seconds
starting query 0 that will take 5 seconds
query 0 has completed, displaying results now
starting query 1 that will take 15 seconds
query 1 timed out, we will email the result of your query when it is completed
starting query 2 that will take 1 seconds
query 2 has completed, displaying results now
starting query 3 that will take 15 seconds
query 1 has completed, emailing result now
query 3 timed out, we will email the result of your query when it is completed
query 3 has completed, emailing result now
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/517046.html
上一篇:單獨執行緒中的快速IO操作
