我使用GCD的DispatchWorkItem跟踪正在发送到Firebase的数据。
我要做的第一件事是声明DispatchWorkItem类型的2个类属性,然后当我准备将数据发送到firebase时,我使用值对其进行初始化。
第一个属性名为errorTask。初始化后,将其cancels设置为firebaseTask,然后将其设置为nil,然后显示“errorTask fired”。它具有一个DispatchAsync Timer,如果在此之前未取消errorTask,它将在0.0000000001秒内调用它。
第二个属性名为firebaseTask。初始化后,它包含一个将数据发送到Firebase的函数。如果firebase回调成功,则取消errorTask并将其设置为nil,然后打印一条打印语句“到达firebase回调”。我还检查一下firebaseTask是否被取消了。
问题是errorTask内部的代码始终在到达firebaseTask回调之前运行。 errorTask代码取消了firebaseTask并将其设置为nil,但是由于某些原因,firebaseTask仍然运行。我不知道为什么吗?
打印语句支持以下事实:errorTask首先运行,因为"errorTask fired"总是在"firebase callback was reached"之前打印。
为什么即使errorTask使这些事情发生,firebaseTask仍不会被取消并设置为nil呢?
在我的实际应用程序中,如果用户向Firebase发送一些数据,则会出现事件指示器。一旦达到Firebase回调,事件指示器将被关闭,并向用户显示一条警报,表明已成功。但是,如果事件指示器上没有计时器,并且永远不会到达回调,那么它将永远旋转。 DispatchAsyc after的计时器设置为15秒,如果未达到回调,则会显示错误标签。 10次中有9次始终有效。
firebaseTask被取消并设置为nil,事件指示器将被驳回errorTask代码块关闭actiInd,显示errorLabel,然后取消firebaseTask并将其设置为nil。一旦firebaseTask被取消并设置为nil,我就认为其中的所有内容都会停止,因为从未实现过回调。 这可能是造成我困惑的原因。 看来,即使firebaseTask被取消并设置为nil,someRef?.updateChildValues(...仍在运行,我也需要取消它。var errorTask:DispatchWorkItem?
var firebaseTask:DispatchWorkItem?
@IBAction func buttonPush(_ sender: UIButton) {
// 1. initialize the errorTask to cancel the firebaseTask and set it to nil
errorTask = DispatchWorkItem{ [weak self] in
self?.firebaseTask?.cancel()
self?.firebaseTask = nil
print("errorTask fired")
// present alert that there is a problem
}
// 2. if the errorTask isn't cancelled in 0.0000000001 seconds then run the code inside of it
DispatchQueue.main.asyncAfter(deadline: .now() + 0.0000000001, execute: self.errorTask!)
// 3. initialize the firebaseTask with the function to send the data to firebase
firebaseTask = DispatchWorkItem{ [weak self] in
// 4. Check to see the if firebaseTask was cancelled and if it wasn't then run the code
if self?.firebaseTask?.isCancelled != true{
self?.sendDataToFirebase()
}
// I also tried it WITHOUT using "if firebaseTask?.isCancelled... but the same thing happens
}
// 5. immediately perform the firebaseTask
firebaseTask?.perform()
}
func sendDataToFirebase(){
let someRef = Database.database().reference().child("someRef")
someRef?.updateChildValues(myDict(), withCompletionBlock: {
(error, ref) in
// 6. if the callback to firebase is successful then cancel the errorTask and set it to nil
self.errorTask?.cancel()
self.errorTask? = nil
print("firebase callback was reached")
})
}
最佳答案
这个取消例程并没有按照我怀疑的方式执行。取消DispatchWorkItem时,它不执行抢先取消。当然,它与updateChildValues调用无关。它所做的只是对isCancelled属性执行线程安全设置,如果您手动遍历循环,则可以看到该任务已取消,因此可以定期检查并过早退出。
结果,在任务开始时检查isCancelled并不是非常有用的模式,因为如果尚未创建任务,则没有任何要取消的操作。或者,如果任务已经创建并添加到队列中,并且在队列有机会启动之前被取消,则显然它将被取消但从未启动,因此您将永远无法进行isCancelled测试。如果任务已经开始,则可能在调用isCancelled之前已经通过了cancel测试。
最重要的是,尝试对cancel请求进行计时,以便在任务开始之后但尚未进行isCancelled测试之前准确地接收到它们,这将是徒劳的。您的比赛几乎不可能完美地计时。此外,即使您确实恰好安排了时间,这也仅说明了整个过程的效率低下(百万个cancel请求中只有1个会达到您的预期)。
通常,如果您有要取消的异步任务,则将其包装在异步自定义Operation子类中,并实现一个停止基础任务的cancel方法。与分派(dispatch)队列相比,操作队列为取消异步任务提供了更多优美的模式。但是所有这些都假定底层的异步任务提供了一种取消它的机制,我不知道Firebase是否甚至提供了一种有意义的机制来取消它。我当然没有在他们的任何例子中看到它。因此,所有这些可能都没有意义。
我建议您远离问题中的特定代码模式,并描述您要完成的工作。让我们不要再为解决更广泛的问题而专门尝试解决问题,而是让我们了解更广泛的目标是什么,然后我们可以讨论如何解决这个问题。
顺便说一句,您的示例中还有其他技术问题。
具体来说,我假设您正在主队列上运行它。因此,task.perform()立即在当前队列上运行它。但是,您的DispatchQueue.main.asyncAfter(...)仅在完成主队列上已完成的操作后才能运行。因此,即使您指定了0.0000000001秒的延迟,它实际上也不会在主队列可用之前运行(即,在您的perform在主队列上完成运行并且您已经通过isCancelled测试之后)。
如果要测试运行任务和取消任务之间的竞争,则需要在其他线程上执行取消。例如,您可以尝试:
weak var task: DispatchWorkItem?
let item = DispatchWorkItem {
if (task?.isCancelled ?? true) {
print("canceled")
} else {
print("not canceled in time")
}
}
DispatchQueue.global().asyncAfter(deadline: .now() + 0.00001) {
task?.cancel()
}
task = item
DispatchQueue.main.async {
item.perform()
}
viewDidLoad中进行操作)。isCancelled属性之前,您将很难抓到该任务。如果您真的想以某种可重复的方式来显示取消逻辑,那么我们将不得不人为地实现这一点:weak var task: DispatchWorkItem?
let queue = DispatchQueue(label: "com.domain.app.queue") // create a queue for our test, as we never want to block the main thread
let semaphore = DispatchSemaphore(value: 0)
let item = DispatchWorkItem {
// You'd never do this in a real app, but let's introduce a delay
// long enough to catch the `cancel` between the time the task started.
//
// You could sleep for some interval, or we can introduce a semphore
// to have it not proceed until we send a signal.
print("starting")
semaphore.wait() // wait for a signal before proceeding
// now let's test if it is cancelled or not
if (task?.isCancelled ?? true) {
print("canceled")
} else {
print("not canceled in time")
}
}
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
task?.cancel()
semaphore.signal()
}
task = item
queue.async {
item.perform()
}
isCancelled确实可以工作。isCancelled。如果进行一些较长的过程,通常可以使用isCancelled进程,在该过程中您可以定期检查isCancelled的状态,如果为true则退出。但这不是您的情况。isCancelled不可能实现您期望的目标。
关于ios - 即使已取消并设置为Nil,Swift iOS -DispatchWorkItem仍在运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48844169/
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
似乎无法为此找到有效的答案。我正在阅读Rails教程的第10章第10.1.2节,但似乎无法使邮件程序预览正常工作。我发现处理错误的所有答案都与教程的不同部分相关,我假设我犯的错误正盯着我的脸。我已经完成并将教程中的代码复制/粘贴到相关文件中,但到目前为止,我还看不出我输入的内容与教程中的内容有什么区别。到目前为止,建议是在函数定义中添加或删除参数user,但这并没有解决问题。触发错误的url是http://localhost:3000/rails/mailers/user_mailer/account_activation.http://localhost:3000/rails/mai
我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
我花了几天时间尝试安装ruby1.9.2并让它与gems一起工作:-/我最终放弃了我的MacOSX10.6机器,下面是我的Ubuntu机器上的当前状态。任何建议将不胜感激!#rubytest.rb:29:in`require':nosuchfiletoload--mongo(LoadError)from:29:in`require'fromtest.rb:1:in`'#cattest.rbrequire'mongo'db=Mongo::Connection.new.db("mydb")#gemwhichmongo/usr/local/rvm/gems/ruby-1.9.2-p0/g
print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上
我在思考流量控制的最佳实践。我应该走哪条路?1)不要检查任何东西并让程序失败(更清晰的代码,自然的错误消息):defself.fetch(feed_id)feed=Feed.find(feed_id)feed.fetchend2)通过返回nil静默失败(但是,“CleanCode”说,你永远不应该返回null):defself.fetch(feed_id)returnunlessfeed_idfeed=Feed.find(feed_id)returnunlessfeedfeed.fetchend3)抛出异常(因为不按id查找feed是异常的):defself.fetch(feed_id