Как отменить другие фьючерсы?

#concurrent-ruby

#параллельный-ruby

Вопрос:

Я создаю несколько фьючерсов и ожидаю, что только один из них достигнет желаемой цели.

Как я могу отменить все другие фьючерсы из будущего?

Вот как я создаю фьючерсы:

 jobs = days_to_scan.map{|day|
      Concurrent::Future.execute do
        sleep_time = day.to_f / days_to_scan.count.to_f * seconds_to_complete.to_f        
        sleep (sleep_time)
        if GoogleAPI.new.api_call(@adwords, ad_seeder, visitor, day) 
           # How to cancel other futures here?
        end
      end
    }
 

Ответ №1:

Возможно, я опоздаю на вечеринку, но я все равно отвечу, так как другие люди могут наткнуться на этот вопрос.

Итак, что вы хотите, это, вероятно, принудительное завершение работы пула потоков, как только закончится одно будущее:

 class DailyJobs
  def call
    thread_pool = ::Concurrent::CachedThreadPool.new
    jobs = days_to_scan.map{ |day|
      Concurrent::Future.execute(executor: thread_pool) do
        sleep_time = day.to_f / days_to_scan.count.to_f * seconds_to_complete.to_f        
        sleep (sleep_time)
        if GoogleAPI.new.api_call(@adwords, ad_seeder, visitor, day) 
           # How to cancel other futures here?
           thread_pool.kill
        end
      end
    }
  end
end
 

дело в том, что уничтожение пула потоков на самом деле не рекомендуется и может привести к непредсказуемым результатам

лучший подход — отслеживать, когда выполняется будущее, и игнорировать другие фьючерсы:

 class DailyJobs
  def call
    status = ::Concurrent::AtomicBoolean.new(false)

    days_to_scan.map{ |day|
      Concurrent::Future.execute do
        return if status.true? # Early return so Future does nothing

        sleep_time = day.to_f / days_to_scan.count.to_f * seconds_to_complete.to_f        
        sleep (sleep_time)
        if GoogleAPI.new.api_call(@adwords, ad_seeder, visitor, day)
          # Do your thing
          status.value = true # This will let you know that at least one Future completed
        end
      end
    }
  end
end
 

Стоит отметить, что если это приложение Rails, вы, вероятно, захотите перенести свое будущее на Rails executor, чтобы избежать проблем с автозагрузкой и взаимоблокировкой. Я писал об этом здесь

Ответ №2:

Хорошо, я мог бы реализовать это как:

 #wait until one job has achieved the goal
while jobs.select{|job| job.value == 'L' }.count == 0 amp;amp; jobs.select{|job| [:rejected, :fulfilled].include?(job.state) }.count != jobs.count   
    sleep(0.1)  
end
#cancel other jobs    
jobs.each{|job| job.cancel unless (job.state == :fulfilled amp;amp; job.value == success_value) }    
}