Ошибка обновления переменной dispatch_async — потокобезопасность

Когда я запускаю следующий код несколько раз, приложение падает в строке:

res.append(i)

Ошибка является фатальной ошибкой: UnsafeMutablePointer.destroy с отрицательным счетчиком или освобождаемым указателем не был выделен *** установить точку останова в malloc_error_break для отладки

Неправильно ли обновлять глобальную переменную внутри dispatch_async?

класс ViewController: UIViewController {

var result = Array<Int>()


func method(completion: (inner: () throws -> String)->Void){
    let group:dispatch_group_t = dispatch_group_create();
    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    var res = Array<Int>()
    for i in 0..<4 {
        dispatch_group_async(group,queue){
            res.append(i)
            print(res)
            print("Block\(i)");                
            var s = 0
            for k in 0..<1000 {
                s = 2+3
            }
            print("Block\(i)End");



        }
    }

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

    print("All background tasks are done!!");
    print(res)
}


override func viewDidLoad() {
    super.viewDidLoad()


    self.method() { (inner: () throws -> String) -> Void in
        do {
            let res = try inner()
            print(res)
        } catch let error {
            print(error)
        }
    }
}

person user584263    schedule 01.07.2016    source источник
comment
основная проблема многопоточности - если не указано иное, НЕ предполагайте, что объект является потокобезопасным, и не обновляйте его из нескольких потоков.   -  person Daij-Djan    schedule 01.07.2016


Ответы (1)


да, Array не является потокобезопасным, поэтому при записи в массив следует обеспечить atomic.

Таким образом, вы можете добавить блокировку высокой производительности: dispatch_semaphore_t.

func method(completion: (inner: () throws -> String)->Void){
    //  add lock
    let lock: dispatch_semaphore_t = dispatch_semaphore_create(1)
    let group:dispatch_group_t = dispatch_group_create();
    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    var res = Array<Int>()
    for i in 0 ..< 5 {
        dispatch_group_async(group,queue){
            // lock
            dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER)
            res.append(i)
            // unlock
            dispatch_semaphore_signal(lock)
            var s = 0
            for k in 0..<1000 {
                s = 2+3
            }
        }
    }

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

    print("All background tasks are done!!");
    print(res)
}

Но будьте осторожны, если ваша асинхронная задача не требует много времени, как описано выше, не используйте многопоточность, потому что планирование потоков занимает много времени и может привести к потере производительности.

person maquannene    schedule 01.07.2016
comment
Спасибо. Это был просто пример кода. Итак, если я получаю доступ к большому количеству глобальных переменных внутри dispatch_async, что является лучшим вариантом? Должен ли я использовать NSOperationQueue? - person user584263; 01.07.2016
comment
Я не уверен, что многие переменные, о которых вы упоминаете, являются многопоточными. Если нет, то, как правило, существует два способа обработки: 1. использование параллельной очереди + блокировка, как указано выше. 2. использование последовательной очереди также может обеспечить атомарность. Или вам нужно использовать другой план в соответствии с вашими требованиями. Вы можете прочитать эту серию статей raywenderlich.com/60749 /grand-central-dispatch-in-depth-part-1, может быть полезно. - person maquannene; 01.07.2016