Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading Swift中具有并发性的功能/代码设计_Multithreading_Swift_Concurrency_Nsurlsession - Fatal编程技术网

Multithreading Swift中具有并发性的功能/代码设计

Multithreading Swift中具有并发性的功能/代码设计,multithreading,swift,concurrency,nsurlsession,Multithreading,Swift,Concurrency,Nsurlsession,我正在尝试在Swift中创建我的第一个应用程序,它涉及到向一个网站发出多个请求。这些请求都是使用块完成的 var task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in ... } task.resume() 据我所知,此块使用的线程与主线程不同 我的问题是,设计依赖于该块中的值的代码的最佳方法是什么?例如,

我正在尝试在Swift中创建我的第一个应用程序,它涉及到向一个网站发出多个请求。这些请求都是使用块完成的

var task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in ... } 
task.resume()
据我所知,此块使用的线程与主线程不同

我的问题是,设计依赖于该块中的值的代码的最佳方法是什么?例如,理想的设计(但由于执行这些块的线程不是主线程,因此不可能实现)是

但是,在上述设计中,最有可能的是getNames()和getEmails()将返回nil,因为任务在返回时不会更新电子邮件/名称

另一种设计(我目前正在实施)是有效地删除“prepareEmails”功能,并在任务功能中按顺序执行所有操作

func prepareEmails() {
    getNames()
}

func getNames() {
    ...
    var task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in 
        getEmails(names)
    }) 
    task.resume()
}

func getEmails(names: NSArray) {
    ...
    var task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in 
        sendEmails(emails, names)
    }) 
    task.resume()
}

有没有比后者更有效的设计?这是我第一次体验并发,因此任何建议都将不胜感激。

调用具有
completionHandler
参数的异步方法时,典型的模式是自己使用
completionHandler
闭包模式。因此,这些方法不返回任何内容,而是使用返回的信息作为参数调用闭包:

func getNames(completionHandler:(NSArray!)->()) {
    ....
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {data, response, error -> Void in 
        let names = ...
        completionHandler(names)
    }
    task.resume()
}

func getEmails(completionHandler:(NSArray!)->()) {
    ....
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {data, response, error -> Void in 
        let emails = ...
        completionHandler(emails)
    }
    task.resume()
}
然后,如果您需要按照代码示例的建议按顺序执行这些操作(即,如果电子邮件的检索取决于
getNames
返回的名称),您可以执行以下操作:

func prepareEmails() {
    getNames() { names in 
        getEmails() {emails in
            sendEmails(names, emails) // I'm assuming the names and emails are in the input to this method
        }
    }
}
或者,如果它们可以同时运行,那么您应该这样做,因为这样会更快。诀窍是如何使第三个任务依赖于另外两个异步任务。两种传统的替代方案包括

  • 将每个异步任务包装到其自己的异步
    NSOperation
    ,然后根据其他两个操作创建第三个任务。这可能超出了问题的范围,但您可以参考《并发编程指南》的一节,或参阅的异步与同步操作和子类化注释部分

  • 使用调度组,在每个请求之前输入组,将组保留在每个请求的完成处理程序中,然后添加调度组通知块(当所有组“enter”调用与其对应的“leave”调用匹配时调用):

  • 坦率地说,如果有任何方法可以在一个网络请求中同时检索电子邮件和姓名,那将更加高效。但是如果你被两个不同的请求所困扰,你可以做类似上面的事情

    注意,我通常不会在Swift代码中使用
    NSArray
    ,而是使用
    String
    对象数组(例如
    [String]
    )。此外,我还加入了错误处理,如果其中一个失败,我将返回错误的性质。但希望这说明了(a)使用
    completionHandler
    块编写自己的方法所涉及的概念;和(b)根据其他两个异步任务的完成情况调用第三位代码。

    上述答案(特别是Rob基于DispatchQueue的答案)描述了并行运行两个任务并对结果作出响应所需的并发概念。为了清晰起见,答案缺乏错误处理,因为传统上,并发问题的正确解决方案非常冗长

    对我来说不是这样


    此代码段管理所有并发,将所有错误路由到
    handleErrorFunc
    ,看起来就像所需的并发模式。

    完美。非常感谢
    func prepareEmails() {
        getNames() { names in 
            getEmails() {emails in
                sendEmails(names, emails) // I'm assuming the names and emails are in the input to this method
            }
        }
    }
    
    func prepareEmails() {
        let group = dispatch_group_create()
    
        var emails: NSArray!
        var names: NSArray!
    
        dispatch_group_enter(group)
        getNames() { results in
            names = results
            dispatch_group_leave(group)
        }
    
        dispatch_group_enter(group)
        getEmails() {results in
            emails = results
            dispatch_group_leave(group)
        }
    
        dispatch_group_notify(group, dispatch_get_main_queue()) {
            if names != nil && emails != nil {
                self.sendEmails(names, emails)
            } else {
                // one or both of those requests failed; tell the user
            }
        }
    }
    
    HoneyBee.start()
            .setErrorHandler(handleErrorFunc)
            .branch {
                 $0.chain(getNames)
                 +
                 $0.chain(getEmails)
            }
            .chain(sendEmails)