在iOS中使用GCD实现Critical部分

时间:2014-04-22 06:08:01

标签: ios objective-c multithreading grand-central-dispatch

我执行的任务非常繁重,我不想阻止主线程。所以我为它启动了一个单独的并发队列。该任务可以有4个实例。

-(dispatch_queue_t)getConcurrentQueue
{
    if(concurrentQueue == nil)
    {
        concurrentQueue = dispatch_queue_create("com.myself.HeavyTask", DISPATCH_QUEUE_CONCURRENT);
    }
    return concurrentQueue;
}

现在开始我的繁重任务 -

-(void)beginTask
{
      //.....
      //.....
        __weak typeof(self) weakSelf = self;
        dispatch_queue_t queue = [self getConcurrentQueue];
        dispatch_async(queue, ^{
            [weakSelf heavyTask];
        });
}

现在方法heavyTask就是这样 -

-(void)heavyTask
{
     //...
     dispatch_sync(dispatch_get_current_queue(), ^{
     // Initialising code for heavy task
     // This is the critical section. Only one of the 4 concurrent threads can enter this at a time
     }
     //....

     while(condition)
     {
        // Perform meat of the task
     }

     //...
     dispatch_sync(dispatch_get_current_queue(), ^{
     // Teardown code. Freeing memory etc.
     // This is also a critical section.
     }
     //...
}

初始化代码和拆解代码使用一些非线程安全的第三方C方法。因此,使它们线程安全不是问题的范围。

现在我已将"initialising code""teardown code"放在 -

dispatch_sync(dispatch_get_current_queue(), ^{
}

我的代码崩溃,我收到错误消息,指出关键部分代码周围没有足够的线程锁定。

我认为dispatch_get_current_queue()不安全,所以我将其替换为concurrentQueue。我也尝试用dispatch_get_main_queue()替换。代码仍然崩溃,抱怨线程锁定不足。

我知道使用GCD实现关键部分的理解存在问题。

任何人都可以清楚地告诉我如何使我的代码在这里正常工作吗?

附带问题 - 我可以在这里使用@synchronized { }块吗?

2 个答案:

答案 0 :(得分:2)

您的代码存在很多问题,包括不遵守命名约定。

基本上,如果你想相互执行相同的并发任务,请使用全局并发队列来执行这些任务。

如果要同时从这些任务(或其他地方)访问共享资源,请定义一个专用队列,例如“sync_queue”,您可以在其中专门访问这些资源。这个“sync_queue”执行你的“关键部分”。

“sync_queue”可以是串行或并发的。

如果您使用串行队列,请使用dispatch_async(sync_queue, block)进行写访问,使用dispatch_sync(sync_queue, block)进行读访问共享资源。

如果您使用并发队列,请使用dispatch_barrier_async(sync_queue, block)进行写访问,使用dispatch_barrier_sync(sync_queue, block)进行读访问共享资源。

示例:

// Read access using a serial sync_queue:
...
__block int counter;
dispatch_sync(sync_queue, ^{
    counter = _counter;
});


// Write access using a serial sync_queue:
...
dispatch_async(sync_queue, ^{
    _counter = counter;
});



// Read access using a concurrent sync_queue:
...
__block int counter;
dispatch_barrier_sync(sync_queue, ^{
    counter = _counter;
});


// Write access using a concurrent sync_queue:
...
dispatch_barrier_async(sync_queue, ^{
    _counter = counter;
});

“繁重任务”的示例:

-(void)heavyTask
{
     dispatch_barrier_async(sync_queue, ^{
         // Initialize heavy task
         ...

         // Continue with the task:
         dispatch_async(dispatch_get_global_queue(0,0), ^{
             BOOL condition = YES;  // condition must be local to the block (it's not a shared resource!)
             while(condition)
             {
                // Perform meat of the task
                condition = ...;
             }

             dispatch_barrier_async(sync_queue, ^{
                 // Teardown code. Freeing memory etc.
                 // This is also a critical section.
                 ...
             }
         });
     }
}

答案 1 :(得分:0)

您将其称为“getSerialQueue”,但实际上您正在其中创建“并发”队列。尝试用getSerialQueue中的DISPATCH_QUEUE_SERIAL代替DISPATCH_QUEUE_CONCURRENT修复它。

请记住:

  

dispatch_sync表示:我将在此处等待此块完成

     

dispatch_async表示:我不会等待

这与并发或串行无关。如果并发队列中的两个任务调用dispatch_sync(block),则'block'将同时执行。

希望这有帮助。