2016-10-14 24 views
3

首先,我是一个初学者,我打算做什么到这里引起我的问​​题的几个假设,这可能听起来很愚蠢,所以请与我裸露。如何一次运行多个后台线程任务? (SWIFT 3)所有的

我通过包含月份的日期String对象的数组2016年10月,这意味着31个String对象试图循环:2016年10月1日... 10月31日2016年对于每一个对象我想要从一些数据数据库并将返回值(也是一个String对象)附加到新数组。但棘手的部分是,新的String对象数组应该与日期数组的顺序完全相同。因此,例如,新的数组中的第五对象应该是在我的日期排列第五对象的新阵列中的返回值(2016年10月5日)和14日对象应该是在我的日期排列的14对象的返回值( 2016年10月14日)等等。显然,数据库检索过程发生在后台线程中,假设系统希望尽快完成整个过程,它会同时触发多个检索任务(全部在自己的线程中)。这样做的问题是它真的会混淆新数组的构建顺序。说实话,它看起来非常混乱,怪异的一部分是数组的顺序不是随机的(这很可能已经证实了我的假设):新阵列的顺序是按照正确的顺序本质上是第8个返回值然后从第9个值的第8个值得到重复,所以有点像这样:

1 October -> 5 
2 October -> 8 
3 October -> 4 
4 October -> 11 
5 October -> 9 
6 October -> 7 
7 October -> 6 
8 October -> 14 
9 October -> 5 
10 October -> 8 
11 October -> 4 
12 October -> 11 
13 October -> 9 
14 October -> 7 
15 October -> 6 
16 October -> 14 
17 October -> 5 
18 October -> 8 
19 October -> 4 
20 October -> 11 
21 October -> 9 
22 October -> 7 
23 October -> 6 
24 October -> 14 
25 October -> 5 
26 October -> 8 
27 October -> 4 
28 October -> 11 
29 October -> 9 
30 October -> 7 
31 October -> 6 

所以你可以在这个模式中看到,它只是取8个不同的值,然后重复一遍,直到阵列充分。如果我在很短的时间内运行整个循环过程两次,那么顺序通常仍然是相同的,除了数组中的第一个值不再相同(所以基本上每个值都会移动1个日期)。无论如何,要切入正题:我假设逐个执行每个检索任务将解决我的问题。这是我当前正在运行的循环:

// Loop through each date 
for date in self.datesToDisplay { 
    // Fire off retrieval method with date object as its only parameter 
    self.getMessagesForDate(date) 
} 

我的模型将返回的值追加到一个新的数组和新创建的数组传递给调用者,就像这样:

// Delegate method which gets called whenever retrieval is finished 
func messagesRetrieved() { 
    // Pass newly created array back to caller 
    self.messagesForDatesToDisplay = self.retrieveModel.messages 
} 

的上面的代码是我在项目中运行的实际代码的一个简化版本,但您明白了。第一个问题是:我是否可以接近正确的方向,并假设可能导致此问题?第二后续问题是:如果我是正确的,我怎么能肯定的是,第二检索过程不会开始,直到第一个全部完成(所以委托方法返回被称为价值和运行后)?

+0

为什么不能使用'NSOperationQueue'来连续添加每个操作。 – Santosh

回答

1

一种方法是检索结果成是不依赖于为了使结果进来即词典的结构。

所以,你可以这样做:

let syncQueue = DispatchQueue(label: "...")  // use dispatch_queue_create() in Swift 2 

let group = DispatchGroup()      // use dispatch_group_create() in Swift 2 

var results = [String: [Message]]() 

for date in datesToDisplay { 
    group.enter()        // use dispatch_group_enter in Swift 2 
    getMessages(for: date) { messages in 
     syncQueue.async {      // use dispatch_async in Swift 2 
      results[date] = messages 
      group.leave()      // use dispatch_group_leave in Swift 2 
     } 
    } 
} 

group.notify(queue: .main) {      // use dispatch_group_notify in Swift 2 
    syncQueue.sync {        // use dispatch_sync in Swift 2 
     // update your model with `results` here 
    } 
    // trigger UI update here 
} 

就个人而言,我只是坚持使用字典结构的结果。

例如,如果我想获得的第三项(“10月3日”),这将是

let oct3Messages = modelDictionary[datesToDisplay[2]] 

但如果你真的觉得有必要将其转换回原来的顺序的排列,你可以这样做:

group.notify(queue: .main) { 
    syncQueue.sync { 
     self.retrieveModel = self.datesToDisplay.map { results[$0]! } 
    } 
    // trigger UI update here 
} 

现在,我在这里做了一些微妙的变化。例如,我为getMessagesForDate添加了一个完成处理程序,所以我会知道请求何时完成,并将结果传递回该结束。您希望避免异步更新模型对象,并且您想知道何时完成所有工作,并使用调度组来协调该工作。我也正在使用同步队列来协调结果的更新以确保线程安全。

但我不想让你迷失在那些细节中。关键的一点是,您应该只想使用不依赖于检索对象顺序的结构。然后,直接从该无序字典中检索(使用您订购的datesToDisplay作为关键字),或者将其转换为有序数组。


为了充分披露,我们应该说它可能比我在这里建议的更复杂。例如,如果您的异步getMessagesForDate仅使用全局队列,则可能需要执行一些操作来约束其中有多少个并发运行。

而且您可能还希望按照执行这些请求的顺序进行基准测试,因为虽然数据库可能在后台线程上运行,但它可能无法相对于彼此并发运行多个请求(通常,数据库将同步它的查询),所以你可能会经历更多的工作。

+0

谢谢!将检索到的对象放入字典中对我来说是个诀窍。我考虑过这样做,但我对使用DispatchGroup类不太熟悉。我最终通过遍历字典并将每个键的值分配给它们合适的索引而将它变回到有序数组,因此我最终做了一些额外的工作,但现在它像一种魅力!再次感谢您的明确解释! –

+0

没问题。顺便说一句,你说:“通过循环字典并赋值”把它变回有序数组“,很明显,你也可以用'map'来完成,就像上面显示的那样,但是如果你有一些工作,那很棒! – Rob

相关问题