如何在 GCD 中停止 DispatchWorkItem?
Posted
技术标签:
【中文标题】如何在 GCD 中停止 DispatchWorkItem?【英文标题】:How to stop a DispatchWorkItem in GCD? 【发布时间】:2016-11-17 02:59:46 【问题描述】:我目前正在玩Grand Central Dispatch,发现了一个名为DispatchWorkItem
的类。文档似乎有点不完整,所以我不确定是否以正确的方式使用它。我创建了以下 sn-p 并期望有所不同。我预计该项目将在调用cancel
后被取消。但由于某种原因,迭代仍在继续。任何想法我做错了什么?代码对我来说似乎很好。
@IBAction func testDispatchItems()
let queue = DispatchQueue.global(attributes:.qosUserInitiated)
let item = DispatchWorkItem [weak self] in
for i in 0...10000000
print(i)
self?.heavyWork()
queue.async(execute: item)
queue.after(walltime: .now() + 2)
item.cancel()
【问题讨论】:
【参考方案1】:没有异步 API 调用“取消”方法将取消正在运行的操作。在每一种情况下,“取消”方法都会做一些事情,以便操作可以查明它是否被取消,并且操作必须不时检查这一点,然后自己停止做更多的工作。
我不知道有问题的 API,但通常类似于
for i in 0...10000000
if (self?.cancelled)
break;
print(i)
self?.heavyWork()
【讨论】:
【参考方案2】:GCD 不执行抢先取消。因此,要停止已经开始的工作项,您必须自己测试是否取消。在 Swift 中,cancel
和 DispatchWorkItem
。在 Objective-C 中,在您使用 dispatch_block_create
创建的块上调用 dispatch_block_cancel
。然后,您可以在 Swift 中使用 isCancelled
(在 Objective-C 中称为 dispatch_block_testcancel
)来测试是否已取消。
func testDispatchItems()
let queue = DispatchQueue.global()
var item: DispatchWorkItem?
// create work item
item = DispatchWorkItem [weak self] in
for i in 0 ... 10_000_000
if item?.isCancelled ?? true break
print(i)
self?.heavyWork()
item = nil // resolve strong reference cycle of the `DispatchWorkItem`
// start it
queue.async(execute: item!)
// after five seconds, stop it if it hasn't already
queue.asyncAfter(deadline: .now() + 5)
item?.cancel()
item = nil
或者,在 Objective-C 中:
- (void)testDispatchItem
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
static dispatch_block_t block = nil; // either static or property
__weak typeof(self) weakSelf = self;
block = dispatch_block_create(0, ^
for (long i = 0; i < 10000000; i++)
if (dispatch_block_testcancel(block)) break;
NSLog(@"%ld", i);
[weakSelf heavyWork];
block = nil;
);
// start it
dispatch_async(queue, block);
// after five seconds, stop it if it hasn't already
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
if (block) dispatch_block_cancel(block);
);
【讨论】:
DispatchWorkItem 取消后可以重复使用吗? @Rob - 这不会创建一个保留周期吗? @mattsven - 很好。你是绝对正确的。不幸的是,在实例化DispatchWorkItem
(解决强引用循环的典型方法)时,您不能使用[weak item]
或[unowned item]
模式,因此您必须在闭包结束时手动nil
item
就像我在修改后的答案中一样。 (但我们可以在 asyncAfter
cancel
的块中使用典型的 [weak item]
模式。)
@Rob 这也是我的第一个想法。不幸的是,如果DispatchItem
在执行之前被取消,内存就会泄漏。
我在代码注释中添加了“DispatchWorkItem”的限定词...以上是关于如何在 GCD 中停止 DispatchWorkItem?的主要内容,如果未能解决你的问题,请参考以下文章