簡體   English   中英

iOS 3.x 中的 GCD 串行調度隊列等價物

[英]Equivalent of GCD serial dispatch queue in iOS 3.x

Apple 的 Grand Central Dispatch (GCD) 很棒,但僅適用於 iOS 4.0 或更高版本。 Apple 的文檔說,“[A] 序列化操作隊列不提供與 Grand Central Dispatch 中的串行調度隊列完全相同的行為”(因為隊列不是 FIFO,但順序由依賴關系和優先級決定)。

在支持 GCD 發布之前的 OS 版本的同時,實現與 GCD 的串行調度隊列相同的效果的正確方法是什么? 或者換一種說法,在想要支持低於 4.0 版本的 iOS 應用程序中處理簡單后台處理(執行 web 服務請求等)的推薦方法是什么?

這個 PseudoSerialQueue 怎么樣? 它是像 Dispatch Serial Queue 一樣的最小實現。

#import <Foundation/Foundation.h>

@interface PseudoTask : NSObject
{
    id target_;
    SEL selector_;
    id queue_;
}

@property (nonatomic, readonly) id target;

- (id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
- (void)exec;
@end

@implementation PseudoTask

@synthesize target=target_;

- (id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
{
    self = [super init];
    if (self) {
        target_ = [target retain];
        selector_ = selector;
        queue_ = [queue retain];
    }
    return self;
}

- (void)exec
{
    [target_ performSelector:selector_];
}

- (void)dealloc
{
    [target_ release];
    [queue_ release];
}
@end

@interface PseudoSerialQueue : NSObject
{
    NSCondition *condition_;
    NSMutableArray *array_;
    NSThread *thread_;
}
- (void)addTask:(id)target selector:(SEL)selector;
@end

@implementation PseudoSerialQueue
- (id)init
{
    self = [super init];
    if (self) {
        array_ = [[NSMutableArray alloc] init];
        condition_ = [[NSCondition alloc] init];
        thread_ = [[NSThread alloc]
            initWithTarget:self selector:@selector(execQueue) object:nil];
        [thread_ start];
    }
    return self;
}

- (void)addTask:(id)target selector:(SEL)selector
{
    [condition_ lock];
    PseudoTask *task = [[PseudoTask alloc]
        initWithTarget:target selector:selector queue:self];
    [array_ addObject:task];
    [condition_ signal];
    [condition_ unlock];
}

- (void)quit
{
    [self addTask:nil selector:nil];
}

- (void)execQueue
{
    for (;;) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        [condition_ lock];
        while (array_.count == 0)
            [condition_ wait];
        PseudoTask *task = [array_ objectAtIndex:0];
        [array_ removeObjectAtIndex:0];
        [condition_ unlock];

        if (!task.target) {
            [pool drain];
            break;
        }

        [task exec];
        [task release];

        [pool drain];
    }
}

- (void)dealloc
{
    [array_ release];
    [condition_ release];
}
@end

如何使用:

PseudoSerialQueue *q = [[[PseudoSerialQueue alloc] init] autorelease];
[q addTask:self selector:@selector(test0)];
[q addTask:self selector:@selector(test1)];
[q addTask:self selector:@selector(test2)];
[q quit];

您可以使用NSOperationQueue對其進行模擬,然后將任務計數設置為 1。

編輯

- 哎呀,應該更仔細地閱讀。 fifo 解決方案如下:

我想不出大多數 ios 開發人員會在您的情況下使用的方法。

我不怕編寫線程程序,所以這是一種解決方案:

  • 創建一個先進先出工作隊列:
    • 支持鎖定
    • 持有一個 NSOperationQueue
    • 擁有一個 NSOperation 子類,旨在在其main的實現中從 fifo 隊列中拉出工人。 一次只能存在一個。
    • 持有要運行的工作人員的 NSArray(定義工作人員由您決定 - 它是 NSInvocation、class、操作......)

NSOperation 子類從 fifo 工作隊列中拉出工作人員,直到 fifo 工作隊列用完。

當 fifo 工作隊列有工人並且沒有活動的子操作時,它會創建一個子操作,並將其添加到其操作隊列中。

如果您不習慣編寫線程程序,則會有一些陷阱——因此,這個解決方案並不適合所有人,但如果您已經習慣使用所有所需的技術,那么編寫這個解決方案不會花費很長時間。

祝你好運

似乎人們會付出很多努力來重寫 NSRunloop。 根據NSRunloop 文檔

您的應用程序不能創建或顯式管理 NSRunLoop 對象。 每個 NSThread object,包括應用程序的主線程,都有一個根據需要自動為其創建的 NSRunLoop object。

因此,毫無疑問,簡單的答案是創建一個可用的隊列:

- (void)startRunLoop:(id)someObject
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    [[NSRunLoop currentRunLoop] run];

    [pool release];
}

...

NSThread *serialDispatchThread = [[NSThread alloc] 
                   initWithTarget:self 
                   selector:@selector(startRunLoop:) 
                   object:nil];
[serialDispatchThread start];

將任務添加到隊列:

[object
    performSelector:@selector(whatever:) 
    onThread:serialDispatchThread
    withObject:someArgument
    waitUntilDone:NO];

根據Run Loops 上的線程編程指南部分

Cocoa 定義了一個自定義輸入源,允許您在任何線程上執行選擇器。 ...執行選擇器請求在目標線程上進行序列化,從而減輕了在一個線程上運行多個方法時可能出現的許多同步問題。

所以你有一個明確的串行隊列。 當然,我的寫得並不出色,因為我已經告訴運行循環永遠運行,你可能更喜歡一個你可以稍后終止的,但這些很容易修改。

有些事情 NSOperationQueue 文檔作者忘記提及,使得這樣的實現看起來微不足道,而實際上並非如此。

僅當從同一線程將 NSOperations 添加到隊列中時,才能保證將最大並發操作計數設置為 1 是串行的。

我正在使用另一個選項,因為它可以正常工作。

從不同的線程添加 NSOperations 但使用 NSCondition 來管理隊列。 startOperations 可以(並且應該,你不想用鎖阻塞主線程)用 performSelectorOnBackgroundThread 調用...

startOperations 方法表示由一個或多個 NSOperations 組成的單個作業。

- (void)startOperations
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    [[AppDelegate condition] lock];

    while (![[[AppDelegate queue] operations] count] <= 0) 
    {
        [[AppDelegate condition] wait];
    }

    NSOperation *newOperation = [alloc, init]....;
    [[AppDelegate queue] addOperation:newOperation];
    [[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don't forget this!

    NSOperation *newOperation1 = [alloc, init]....;
    [[AppDelegate queue] addOperation:newOperation1];
    [[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don't forget this!

    NSOperation *newOperation2 = [alloc, init]....;
    [[AppDelegate queue] addOperation:newOperation2];
    [[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don't forget this!

    // Add whatever number operations you need for this single job

    [[AppDelegate queue] signal];
    [[AppDelegate queue] unlock];

    [NotifyDelegate orWhatever]

    [pool drain];
}

而已!

如果無論如何處理都在后台,你真的需要它嚴格有序嗎? 如果你這樣做了,你可以簡單地通過設置你的依賴來達到同樣的效果,所以 1 依賴於 0、2 依賴於 1、3 依賴於 2 等等。然后強制操作隊列按順序處理它們。 設置最大並發操作數為1,隊列也保證是串行的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM