热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

在swift中创建线程安全数组

如何解决《在swift中创建线程安全数组》经验,为你挑选了4个好方法。

我有快速的线程问题.我有一个包含一些对象的数组.在委托上,类每秒都会获得新对象.之后我必须检查对象是否已经在数组中,所以我必须更新对象,否则我必须删除/添加新对象.

如果我添加一个新对象,我必须首先通过网络获取一些数据.这是handelt经过一个街区.

现在我的问题是,如何同步这项任务?

我尝试了一个dispatch_semaphore,但是这个阻止了UI,直到块完成.

我还尝试了一个简单的bool变量,它检查块当前是否执行并同时跳过compare方法.

但这两种方法都不理想.

什么是管理阵列的最佳方式,我不想在阵列中有重复的数据.



1> skim..:

Kirsteins是正确的,但您并不总是需要使用调度队列.您可以使用:

objc_sync_enter(array)
// manipulate the array
objc_sync_exit(array)

这应该做的伎俩.为了增加额外的好处,您可以创建一个在需要线程安全时使用的函数:

func sync(lock: NSObject, closure: () -> Void) {
    objc_sync_enter(lock)
    closure()
    objc_sync_exit(lock)
}

...
var list = NSMutableArray()
sync (list) {
   list.addObject("something")
}

请注意,我已更改AnyObjectNSObject.在Swift集合类型中实现为structs并且它们按值传递,因此我猜测使用方便函数时使用通过引用传递的可变集合类会更安全.sync

更新Swift

线程安全访问的推荐模式是使用dispatch barrier:

let queue = DispatchQueue(label: "thread-safe-obj", attributes: .concurrent)

// write
queue.async(flags: .barrier) {
    // perform writes on data
}

// read
var value: ValueType!
queue.sync {
    // perform read and assign value
}
return value


在诸如`Array`之类的值类型上使用`objc_sync_enter`是不安全/正确的.`objc_sync_array`执行指针比较,并且不保证桥接Swift值类型的指针的生命周期是稳定的(实际上大多数时候它是*不*稳定).
你让我很好奇,所以我在'objc_sync_enter`上找到了这个:http://rykap.com/objective-c/2015/05/09/synchronized/
这不起作用,原因/sf/ask/17360801/

2> Kirsteins..:

我解决这个问题的方法是使用串行调度队列来同步对盒装数组的访问.当你试图获取索引值并且队列真的很忙时它会阻塞线程,但这也是锁的问题.

public class SynchronizedArray {
    private var array: [T] = []
    private let accessQueue = dispatch_queue_create("SynchronizedArrayAccess", DISPATCH_QUEUE_SERIAL)

    public func append(newElement: T) {
        dispatch_async(self.accessQueue) {
            self.array.append(newElement)
        }
    }

    public subscript(index: Int) -> T {
        set {
            dispatch_async(self.accessQueue) {
                self.array[index] = newValue
            }
        }
        get {
            var element: T!

            dispatch_sync(self.accessQueue) {
                element = self.array[index]
            }

            return element
        }
    }
}

var a = SynchronizedArray()
a.append(1)
a.append(2)
a.append(3)

// can be empty as this is non-thread safe access
println(a.array)

// thread-safe synchonized access
println(a[0])
println(a[1])
println(a[2])


您可以考虑使用读写器模式:使用`DISPATCH_QUEUE_CONCURRENT`; 将写入从`dispatch_async`更改为`dispatch_barrier_async`; 但是将读取保留为`dispatch_sync`.这为您提供了并发读取,但写入仍然是同步的.
你只对`dispatch_barrier_async`进行写操作.但是用`dispatch_sync`读取.因此,虽然写入不会(并且不应该)与其他任何内容同时发生,但是读取_can_与其他读取同时发生.请参阅WWDC 2012视频后半部分中的模式#6 [带块,GCD和XPC的异步设计模式](https://developer.apple.com/videos/play/wwdc2012-712/).

3> rmooney..:

Kirsteins的答案是正确的,但为方便起见,我已经用Amol Chaudhari和Rob的建议更新了答案,建议使用带有异步屏障的并发队列来允许并发读取但阻止写入.

我还包装了一些对我有用的其他数组函数.

public class SynchronizedArray {
private var array: [T] = []
private let accessQueue = dispatch_queue_create("SynchronizedArrayAccess", DISPATCH_QUEUE_CONCURRENT)

public func append(newElement: T) {
    dispatch_barrier_async(self.accessQueue) {
        self.array.append(newElement)
    }
}

public func removeAtIndex(index: Int) {
    dispatch_barrier_async(self.accessQueue) {
        self.array.removeAtIndex(index)
    }
}

public var count: Int {
    var count = 0

    dispatch_sync(self.accessQueue) {
        count = self.array.count
    }

    return count
}

public func first() -> T? {
    var element: T?

    dispatch_sync(self.accessQueue) {
        if !self.array.isEmpty {
            element = self.array[0]
        }
    }

    return element
}

public subscript(index: Int) -> T {
    set {
        dispatch_barrier_async(self.accessQueue) {
            self.array[index] = newValue
        }
    }
    get {
        var element: T!

        dispatch_sync(self.accessQueue) {
            element = self.array[index]
        }

        return element
    }
}
}

更新 这是为Swift3更新的相同代码.

public class SynchronizedArray {
private var array: [T] = []
private let accessQueue = DispatchQueue(label: "SynchronizedArrayAccess", attributes: .concurrent)

public func append(newElement: T) {

    self.accessQueue.async(flags:.barrier) {
        self.array.append(newElement)
    }
}

public func removeAtIndex(index: Int) {

    self.accessQueue.async(flags:.barrier) {
        self.array.remove(at: index)
    }
}

public var count: Int {
    var count = 0

    self.accessQueue.sync {
        count = self.array.count
    }

    return count
}

public func first() -> T? {
    var element: T?

    self.accessQueue.sync {
        if !self.array.isEmpty {
            element = self.array[0]
        }
    }

    return element
}

public subscript(index: Int) -> T {
    set {
        self.accessQueue.async(flags:.barrier) {
            self.array[index] = newValue
        }
    }
    get {
        var element: T!
        self.accessQueue.sync {
            element = self.array[index]
        }

        return element
    }
}
}


removeAtIndex可以删除此代码中的错误项目导致idx冷却被其他线程'removeAtIndex'更改...
@mooney我认为@Speakus就在这里:说sychronized数组有一个元素,这不会失败吗?`if synchronizedArray.count == 1 {synchronizedArray.remove(at:0)}`这是一个竞争条件,比如两个线程执行该语句.两者同时读取1的计数,两者同时将写入块排队.写块顺序执行,第二个将失败.我错了吗?这是设计者还是用户错误?在任何一种情况下它都很脆弱,让我永远不想触摸多线程...你怎么能让这个安全?回到阅读和写作的独家锁?

4> nbloqs..:

一个小细节:在Swift 3中(至少在XCode 8 Beta 6中),队列的语法发生了显着变化.@Kirsteins答案的重要变化是:

private let accessQueue = DispatchQueue(label: "SynchronizedArrayAccess")

txAccessQueue.async() {
  // Your async code goes here...
}

txAccessQueue.sync() {
  // Your sync code goes here...
}


推荐阅读
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • LeetCode笔记:剑指Offer 41. 数据流中的中位数(Java、堆、优先队列、知识点)
    本文介绍了LeetCode剑指Offer 41题的解题思路和代码实现,主要涉及了Java中的优先队列和堆排序的知识点。优先队列是Queue接口的实现,可以对其中的元素进行排序,采用小顶堆的方式进行排序。本文还介绍了Java中queue的offer、poll、add、remove、element、peek等方法的区别和用法。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
author-avatar
临冬将至
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有