在执行另一个块之前执行所有后台线程

Posted

技术标签:

【中文标题】在执行另一个块之前执行所有后台线程【英文标题】:Execute all background threads before executing another block 【发布时间】:2016-04-24 10:17:23 【问题描述】:

我正在尝试在 Core Data 中加载照片,但这需要一些后台线程,所以碰巧我的对象在没有照片的情况下保存在 Core Data 中,我得到那张照片是 nil。我小组的照片也是如此。

简而言之,在每次迭代时,我都会在 Core Data 中保存一个对象,并且保存速度比加载我的照片数据要快。

我尝试了 Semaphors、障碍、组,但没有一个奏效。我当然做错了什么,但我不知道是什么。如果有人可以帮助我,我将不胜感激,我已经为同样的问题苦苦挣扎了 2 周。这是我的简化版代码,完整版可在下面访问:

// MARK - ViewDidLoad
override func viewDidLoad() 
    // This method is only called once, so you want to create any controls or arrays here
    super.viewDidLoad()

    // Verifying connectivity to internet
    if Reachability.isConnectedToNetwork() == true 

    // Getting data from Parse + getDataInBackgroundWithBlock

    else

    



// MARK - Action of the login button
@IBAction func loginBtn_click(sender: AnyObject) 
    if usernameTxt.text == "" || passwordTxt.text == "" 
        self.displayAlert("Error", message: "Please enter a username and password")
    else

        // MARK - Login the user
        PFUser.logInWithUsernameInBackground(usernameTxt.text!, password: passwordTxt.text!)  (user, error) -> Void in

            if error == nil 

                let queue = dispatch_queue_create("com.karagan.app.queue", DISPATCH_QUEUE_CONCURRENT)

                dispatch_async(queue,  () -> Void in
                    self.getObjectsFromQueryWithAscendingOrder("Add", key: "user", value: userName, ascendingOrder: "added", completionHandler:  (objects1) -> Void in

                // Update of an array in background 

                    )
                )



                dispatch_async(queue,  () -> Void in

               // Update of an array in background

                )



                dispatch_async(queue,  () -> Void in
                    self.getObjectsFromQueryWithAscendingOrder("Group", key: "membersUsername", value: userName, ascendingOrder: "membersUsername", completionHandler:  (objects3) -> Void in

                 // Update of an array in background

                        // MARK - Adding all the users in Core data
                        for var i = 0; i < usersFromParseDataBase.count; i++ 

                            let entity = NSEntityDescription.entityForName("Users", inManagedObjectContext: self.managedObjectContext)
                            let newUser = Users(entity: entity!, insertIntoManagedObjectContext: self.managedObjectContext)
                            if self.arrayAddedUsers.contains(usersFromParseDataBase[i].username)

                // Saving new records in Core Data                  

                            
                        
                    )
                )

                dispatch_async(queue,  () -> Void in
                    self.getObjectsFromQueryWithDescendingOrder("Group", key: "membersUsername", value: userName, descendingOrder: "conversationUpdate", completionHandler:  (objects4) -> Void in

                            if array.count == 2 

                    // Getting data in background - photo.getDataInbackgroundWithBlock                  

                                
                            else

                // Getting data in background - photo.getDataInbackgroundWithBlock

                            
                        
                    )
                )

                dispatch_barrier_async(queue,  () -> Void in

        // Doing some stuff in background which needs the data from threads above 

                )

            else
                // Print error from loginWithUsernaemInBackground
            
        
    

这是在日志中显示的内容,因此您可以清楚地看到屏障在图片加载完成之前执行。

TEST 3 - interacting user has been saved to core data
Loading profile image
Loading profile image
Loading group image
Loading profile image
Loading profile image
9
5
Loading group image
Loading group image
Loading group image
Loading group image

问题是我以后需要这些数据进行其他操作。我的代码不一致。

【问题讨论】:

您可能需要查看 dispatch_group_create()、dispatch_group_wait() 和 dispatch_group_async(),以便在继续之前拥有一组您希望等待完成的任务。此链接可能会有所帮助:***.com/questions/11909629/… 如果您正在调度的代码块本身是异步任务,您可能必须明确地dispatch_group_enter/leave。如果不查看生成这些日志消息的代码,将很难诊断您的问题或提供进一步的建议。或者,坦率地说,因为您的代码非常复杂,您可能想要构造 a much simpler, reproducible example 来显示您描述的那种错误。 你好 Rob,我添加了一个更简单的代码版本,其中只有最重要的部分。是否全面?如果没有,我可以尝试使它更简单。谢谢你的帮助。 dee zg,我已经尝试过 dispatch_group_create() ......但我仍然遇到同样的问题。只是我不知道如何正确实现它。除非我在最后一个块中添加“sleep(Uint32)”,否则我总是遇到同样的问题,但这并不一致。 如果有人找到我的问题的解决方案,我将非常感激,因为我被这个问题困扰了 3-4 周,不幸的是,关于堆栈溢出的问题都没有帮助。跨度> 【参考方案1】:

我认为你是在正确的轨道上。当我创建一个简单的程序并使用dispatch_barrier_async 时,它可以完美运行:

let queue = dispatch_queue_create("com.karagan.app.queue", DISPATCH_QUEUE_CONCURRENT)

dispatch_async(queue,  () -> Void in
    sleep(1)
    print("1")
)

dispatch_async(queue,  () -> Void in
    sleep(2)
    print("2")
)

dispatch_async(queue,  () -> Void in
    sleep(3)
    print("3")
)

dispatch_async(queue,  () -> Void in
    sleep(4)
    print("4")
)

dispatch_barrier_async(queue,  () -> Void in
    print("done")
)

输出:

1
2
3
4
done

我认为问题在于dispatch_async 没有等待对PFFile.getDataInBackgroundWithBlock 的调用完成:

photoFile.getDataInBackgroundWithBlock( (imageData, error) -> Void in
    if error == nil 
        self.arrayImageFiles.append(imageData!)
        print("Loading group image")
    
)

尝试将所有调用切换到getDataInBackgroundWithBlock,并改为同步调用PFFile.getData (https://parse.com/docs/osx/api/Categories/PFFile%28Synchronous%29.html#/c:objc%28cs%29PFFile%28im%29getData:):

var imageData = photoFile.getData
if (imageData != nil) 
    self.arrayImageFiles.append(imageData!)

这应该不会有任何性能问题,因为您已经在异步执行此代码。

【讨论】:

感谢马克的回答。我仍然需要做一些测试,但这可能对我有帮助。我不知道是否可以在主线程上加载图像数据。非常感谢您的帮助,如果它真的解决了我的问题,我会及时通知您。 使用dispatch_barrier_async只能用作“哨兵”,如果之前提交到同一队列的任务是同步的。不过,在 OP 的情况下,它们是异步的。也就是说,调用立即返回,并且——对于队列——这个块已经完成。 @CouchDeveloper 这就是我在回答中所说的,也是我建议使这些任务同步的原因。我错过了什么吗? 网络请求不应同步执行。一种可行的方法是使用@dezg 在她/他对 OPs 问题的评论中建议的调度组。链接指向的答案是正确的,并且解释得很好。 因此,在这种情况下,由于网络调用是在异步块中完成的,它不应该冻结 UI,但它会强制每个块中的图像等待相同的前一个图像块加载。

以上是关于在执行另一个块之前执行所有后台线程的主要内容,如果未能解决你的问题,请参考以下文章

java-守护线程

什么是 Daemon 线程?它有什么意义?

004 后台线程

C#异步执行代码块

c#线程之前台线程后台线程及使用

后台线程(daemon)