草庐IT

ios - 如何获取多个 DispatchWorkItems 的取消状态

coder 2024-01-28 原文

背景

我正在实现搜索。每个搜索查询都会产生一个 DispatchWorkItem,然后排队等待执行。由于用户触发新搜索的速度比完成前一个搜索的速度快,因此我想在收到新搜索后立即取消前一个搜索。

这是我当前的设置:

var currentSearchJob: DispatchWorkItem?
let searchJobQueue = DispatchQueue(label: QUEUE_KEY)

func updateSearchResults(for searchController: UISearchController) {
    let queryString = searchController.searchBar.text?.lowercased() ?? ""

    // if there is already an (older) search job running, cancel it
    currentSearchJob?.cancel()

    // create a new search job
    currentSearchJob = DispatchWorkItem() {
        self.filter(queryString: queryString)
    }

    // start the new job
    searchJobQueue.async(execute: currentSearchJob!)
}

问题

我知道 dispatchWorkItem.cancel() 不会立即终止正在运行的任务。相反,我需要手动检查 dispatchWorkItem.isCancelled。但是在这种情况下,如何获得正确的 dispatchWorkItem 对象?

如果我只设置一次 currentSearchJob,我可以简单地访问该属性,就像 done in this case .但是,这在这里不适用,因为该属性将在 filter() 方法完成之前被覆盖。 我如何知道哪个实例实际运行了我要在其中检查 dispatchWorkItem.isCancelled 的代码?

理想情况下,我想提供新创建的 DispatchWorkItem 作为 filter() 方法的附加参数。但这是不可能的,因为我会得到一个 Variable used within its own initial value 错误。

我是 Swift 的新手,所以我希望我只是遗漏了一些东西。非常感谢任何帮助!

最佳答案

诀窍是如何让已分派(dispatch)的任务检查它是否已被取消。我实际上建议考虑 OperationQueue方法,而不是直接使用调度队列。

至少有两种方法:

  • 最优雅的,恕我直言,就是子类化 Operation ,在 init 方法中传递你想要的任何东西,并在 main 中执行工作方法:

     class SearchOperation: Operation {
         private var queryString: String
    
         init(queryString: String) { 
             self.queryString = queryString
             super.init()
         }
    
         override func main() {
             // do something synchronous, periodically checking `isCancelled`
             // e.g., for illustrative purposes
    
             print("starting \(queryString)")
             for i in 0 ... 10 {
                 if isCancelled { print("canceled \(queryString)"); return }
                 print("  \(queryString): \(i)")
                 heavyWork()
             }
             print("finished \(queryString)")
         }
    
         func heavyWork() {
             Thread.sleep(forTimeInterval: 0.5)
         }
     }
    

    因为它在 Operation 中子类,isCancelled隐式引用自身而不是一些ivar,避免对其检查的内容产生任何混淆。而您的“开始新查询”代码可以只说“取消相关操作队列中当前的任何内容,并将新操作添加到该队列”:

     private var searchQueue: OperationQueue = {
         let queue = OperationQueue()
         // queue.maxConcurrentOperationCount = 1  // make it serial if you want
         queue.name = Bundle.main.bundleIdentifier! + ".backgroundQueue"
         return queue
     }()
    
     func performSearch(for queryString: String) {
         searchQueue.cancelAllOperations()
         let operation = SearchOperation(queryString: queryString)
         searchQueue.addOperation(operation)
     }
    

    我推荐这种方法,因为您最终会得到一个小的内聚对象,即操作,它本着单一职责原则的精神很好地封装了您想要完成的工作 block 。

  • 虽然以下不太优雅,但从技术上讲,您还可以使用基于 block 的 BlockOperation,但您可以将操作的创建和添加关闭操作。使用这种技术,您实际上可以将对该操作的引用传递给它自己的闭包:

     private weak var lastOperation: Operation?
    
     func performSearch(for queryString: String) {
         lastOperation?.cancel()
    
         let operation = BlockOperation()
         operation.addExecutionBlock { [weak operation, weak self] in
             print("starting \(identifier)")
             for i in 0 ... 10 {
                 if operation?.isCancelled ?? true { print("canceled \(identifier)"); return }
                 print("  \(identifier): \(i)")
                 self?.heavyWork()
             }
             print("finished \(identifier)")
         }
         searchQueue.addOperation(operation)
         lastOperation = operation
     }
    
     func heavyWork() {
         Thread.sleep(forTimeInterval: 0.5)
     }
    

    为了完整起见,我只提到这一点。我认为 Operation 子类方法通常是更好的设计。我将使用 BlockOperation 来处理一次性的事情,但一旦我想要更复杂的取消逻辑,我认为 Operation 子类方法更好。

我还应该提到,除了更优雅的取消功能之外,Operation 对象还提供各种其他复杂的功能(例如,异步管理本身是异步的任务队列;限制并发性等)。这都超出了这个问题的范围。

关于ios - 如何获取多个 DispatchWorkItems 的取消状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53762258/

有关ios - 如何获取多个 DispatchWorkItems 的取消状态的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  6. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  7. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  8. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  9. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  10. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

随机推荐