NSOperation – 强制操作等待其他人dynamic
我想实现一个操作队列,我有以下情况:
NSOperation A NSOperation B NSOperation C NSOperation D NSOperationQueue queue
我开始添加A
queue
。
在A
的执行过程中,我需要从B
获取一些数据,而我不能继续使用A
直到B
返回我需要的数据。
B
取决于C
和C
取决于D
也会出现相同的情况。
为了pipe理这个,在每个NSOperation
我有这个代码:
NSOperation *operation; //This can be A, B, C, D or any other NSOperation [self setQueuePriority:NSOperationQueuePriorityVeryLow]; //Set the current NSOperation with low priority [queue addOperation: operation]; //Add the operation that I want to the queue while(!operation.isFinished && !self.isCancelled){} //I need to wait the operation that I depend before moving on with the current operation [self setQueuePriority:NSOperationQueuePriorityNormal]; //After the while, the other operation finished so I return my priority to normal and continue if(self.isCancelled){ //If I get out of the while because the current operation was cancelled I also cancel the other operation. [operation cancel]; }
我的问题是,当我有像3或4 NSOperations
等待和执行while(!operacao.isFinished && !self.isCancelled){}
我的代码只是冻结,因为对我来说很重要的NSOperation不会被执行,甚至如果它有更高的优先权。
我试过了
-
在执行期间添加依赖关系,但是因为我的NSOperation已经在运行了,我似乎没有任何效果。
-
而不是将操作添加到队列中,我可以执行一些
[operation start]
。 它的工作,但取消当前的操作也将取消我开始的其他操作? -
我可以做一些像
while(!operacao.isFinished && !self.isCancelled){[NSThread sleepForTimeInterval:0.001];}
。 它有效,但这是正确的方法吗? 也许有更好的解决办法。
在这种情况下,我怎么能保证我想要的操作将会运行,其他人将在后台等待呢? 什么是解决这个问题的正确方法?
如果有人问我为什么我不在开始我的队列之前添加依赖关系,因为只有在某些条件为真的情况下,操作才会需要另一个。 我只知道在执行期间是否需要其他操作。
谢谢你的时间。
这里有两个想法与人为的例子。 我只使用了两个操作,但是可以将该概念扩展为任意数量和/或根据需要嵌套它们。
示例1:使用Grand Central调度
GCD提供了轻量级的“调度组”,允许您明确地sorting任务,然后等待完成。 在这种情况下,AlphaOperation会创build一个组并将其input,然后启动BetaOperation,其CompletionBlock将导致组保留。 当你调用dispatch_group_wait
,当前线程会阻塞,直到进入组的次数等于离开它的次数(很像保留计数)。 在任何可能长时间运行的任务之后,不要忘记检查操作的isCancelled
状态。
@interface BetaOperation : NSOperation @end @implementation BetaOperation - (void)main { NSLog(@"beta operation finishing"); } @end @interface AlphaOperation : NSOperation @end @implementation AlphaOperation - (void)main { dispatch_group_t group = dispatch_group_create(); dispatch_group_enter(group); BetaOperation *betaOperation = [[BetaOperation alloc] init]; betaOperation.completionBlock = ^{ dispatch_group_leave(group); }; [betaOperation start]; dispatch_group_wait(group, DISPATCH_TIME_FOREVER); if ([self isCancelled]) return; NSLog(@"alpha operation finishing"); } @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { dispatch_async(dispatch_get_main_queue(), ^{ AlphaOperation *operation = [[AlphaOperation alloc] init]; [operation start]; }); return YES; } @end
示例2:使用本地NSOperationQueue
由于您已经在使用工作操作,所以另一种select是将队列创build为AlphaOperation的属性,然后添加BetaOperation并在队列上调用waitUntilAllOperationsAreFinished
。 这有一个额外的好处,你可以轻松取消AlphaOperation取消队列的操作时,只需重写cancel
方法。
@interface BetaOperation : NSOperation @end @implementation BetaOperation - (void)main { NSLog(@"beta operation finishing"); } @end @interface AlphaOperation : NSOperation @property (strong) NSOperationQueue *queue; @end @implementation AlphaOperation - (void)main { self.queue = [[NSOperationQueue alloc] init]; BetaOperation *betaOperation = [[BetaOperation alloc] init]; [self.queue addOperation:betaOperation]; [self.queue waitUntilAllOperationsAreFinished]; if ([self isCancelled]) return; NSLog(@"alpha operation finishing"); } - (void)cancel { [super cancel]; [self.queue cancelAllOperations]; } @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { dispatch_async(dispatch_get_main_queue(), ^{ AlphaOperation *operation = [[AlphaOperation alloc] init]; [operation start]; }); return YES; } @end
一种方法是从操作类之外进行pipe理。 在创buildA / B / C / D时正确设置操作依赖关系。
步骤:(在创build这些操作的方法中)
1)创build操作A
2)如果操作B提供的数据不可用,则创build操作B,并根据操作B进行操作A. 像operationA.addDependency(operationB);
3)。 对C和D重复步骤2(即,如果需要,B取决于C,而C取决于D)
4)将操作添加到队列中。 队列将根据依赖关系执行,即。 D,C,B,A
尝试使用setCompletionBlock:
像这样:
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSOperation *operationA; NSOperation *operationB; //... initialize operationA and operationB however you please ... [operationA setCompletionBlock:^{ if ([operationA satisfiesSomeCriteria]) { [queue addOperation:operationB]; } }]; [queue addOperation:operationA];
在操作上设置完成块时,在操作的主要任务完成或取消后执行。 因此,正在执行的操作的结果是可用的,所以您可以决定是否将下一个操作添加到队列中。
所以基本上你只需要确保第一个在开始下一个之前完成。 除非你不告诉它,否则NSOperationQueue会并行运行。 您可以在您的操作队列上调用setMaxConcurrentOperationCount:并将其设置为1,以基本将其转换为一次只能运行一个操作的串行队列。
一旦国家统计局采取主要的方法,你就必须经历这个过程。 没有暂停状态,只能完成或取消。
我将在操作A上实现一个NSCopying,将整个状态复制到一个新的实例中。 您将有一个委托方法或块能够沟通,此操作无法通过,因为它是从操作B中缺less信息。
所以这个过程会这样:
- 创build操作A,设置委托
- 你不能继续,委托方法触发
- 委托创build一个新的操作B,创build一个操作A的副本,设置依赖,使得A将等待B的完成
- 那么委托取消原来的操作A
在委托内部,您必须确保暂停队列以避免竞争状况。 上述步骤之后,您恢复队列。 在操作A中,您将有多个检查isCancelled的地方在取消时实际上不再执行主要的工作。
我认为你是一个错误的方法。如果队列中的每个操作都有一个优先级,并且必须按顺序执行,为什么不使用4个不同的线程呢?
以一个代表状态的伊娃(0:没有操作完成,1:一个操作完成,等等),用一个条件保护它:
@property(nonatomic,strong) NSCondition* condition; @property (nonatomic) NSUInteger state;
初始化一切(状态从零开始),然后创build具有不同优先级的4个不同的线程。这是由线程A执行的select器的一个例子:
- (void) threadA : (id) sender { [condition lock]; while(state!=3) { [condition wait]; } // Do the job here state=4; // That's kinda useless but useful if in future you // want another thread that starts doing the job when A ends [condition unlock]; }
所以所有得到执行的顺序,你想要的。
编辑
你可以做相当于我在这里做的,但使用NSOperationQueue:
NSOperationQueue* queue=[NSOperationQueue new]; [queue setMaxConcurrentOperationCount: 4]; [queue addOperation: [[NSInvocationOperation alloc]initWithTarget: self selector: @selector(threadA:) object: nil]]
通过说你是错误的方法我的意思是你不应该使用1作为maxConcurrentOperationCount队列。 主队列将这个值设置为1,这就是你的麻烦的原因。
正如你所发现的那样,你不能在依赖关系中这样做,因为这只会影响到一个操作的开始 – 如果你不知道你需要子操作,直到主操作运行,所以这是不好的。 单个操作队列无法解决此问题。
但是,由于您已经在运行队列上运行,因此不需要将其他操作添加到队列中。 只要在原地同步执行它们。 你必须等他们回来,为什么不呢?