xml地图|网站地图|网站标签 [设为首页] [加入收藏]
的线程安全问题,两数相除
分类:编程

  

近一个月一直在写业务,空闲时间刷刷leetcode,刷题过程中遇到了一道比较有意思的题目,和大家分享。

Swift Array copy 的线程安全问题

澳门新葡亰手机版,NSArray 继承自 NSObject,属于对象,有 copy 方法。Swift 的 Array 是 struct,没有 copy 方法。把一个 Array 变量赋值给另一个变量,两个变量的内存地址相同吗?与此相关的有多线程安全问题。本文探究这两个问题。

/*

 

内存地址

定义测试 class 和 struct

class MyClass {

    var intArr = [Int]()
    var structArr = [MyStructElement]()
    var objectArr = [MyClassElement]()
}

struct MyStructElement {}

class MyClassElement {}

定义输出内存地址的 closure

let memoryAddress: (Any) -> String = {
    guard let cVarArg = $0 as? CVarArg else { return "Can not find memory address" }
    return String(format: "%p", cVarArg)
}

 隶属于每一个类或结构体的函数称之为方法:

题目描述:

测试 Int array

private func testIntArr() {
    print(#function)

    let my = MyClass()
    for i in 0...10000 {
        my.intArr.append(i)
    }
    print("Before arr address:", memoryAddress(my.intArr))

    // Copy Array is NOT thread safe
    let arr = my.intArr // If move this into async closure, crash
    print("Temp   arr address:", memoryAddress(arr)) // Copy. Address different from my.intArr
    DispatchQueue.global().async {
        var sum = 0
        for i in arr {
            sum += i
        }
        print("Sum:", sum) // 0 + 1 + ... + 10000 = 50005000
    }

    my.intArr.removeAll()
    for _ in 0...10000 {
        my.intArr.append(0)
    }
    print("After  arr address:", memoryAddress(my.intArr)) // New address
}

在 view controller 中进行测试

override func viewDidLoad() {
    super.viewDidLoad()

    for _ in 0...1000 {
        testIntArr()
    }
}

结果

澳门新葡亰手机版 1

Int array 的内存地址不同,赋值过程发生了 copy。

 方法分为类方法和实例方法, 对应OC中的+ - 方法

给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。返回被除数 dividend 除以除数 divisor 得到的商。

测试 struct array

private func testStructArr() {
    print(#function)

    let my = MyClass()
    for _ in 0...10000 {
        my.structArr.append(MyStructElement())
    }
    print("Before arr address:", memoryAddress(my.structArr))

    // Copy Array is NOT thread safe
    let arr = my.structArr // If move this into async closure, crash
    print("Temp   arr address:", memoryAddress(arr)) // Copy. Address different from my.structArr
    DispatchQueue.global().async {
        var sum = 0
        for _ in arr {
            sum += 1
        }
        print("Sum:", sum) // 10001
    }

    my.structArr.removeAll()
    for _ in 0...10000 {
        my.structArr.append(MyStructElement())
    }
    print("After  arr address:", memoryAddress(my.structArr)) // New address
}

在 view controller 中进行测试

override func viewDidLoad() {
    super.viewDidLoad()

    for _ in 0...1000 {
        testStructArr()
    }
}

结果

澳门新葡亰手机版 2

Struct array 的内存地址不同,赋值过程发生了 copy。

 实例方法:实例方法一定是通过对象来调用的, 实例方法隶属于某一个类

示例 1:

测试 Object array

private func testObjectArr() {
    print(#function)

    let my = MyClass()
    for _ in 0...10000 {
        my.objectArr.append(MyClassElement())
    }
    print("Before arr address:", memoryAddress(my.objectArr))

    // Copy Array is NOT thread safe
    let arr = my.objectArr // If move this into async closure, crash
    print("Temp   arr address:", memoryAddress(arr)) // Not copy. Same as my.objectArr
    DispatchQueue.global().async {
        var sum = 0
        for _ in arr {
            sum += 1
        }
        print("Sum:", sum) // 10001
    }

    my.objectArr.removeAll()
    for _ in 0...10000 {
        my.objectArr.append(MyClassElement())
    }
    print("After  arr address:", memoryAddress(my.objectArr)) // New address
}

在 view controller 中进行测试

override func viewDidLoad() {
    super.viewDidLoad()

    for _ in 0...1000 {
        testObjectArr()
    }
}

结果

澳门新葡亰手机版 3

一个 object array 变量赋值给另一个变量,两个变量的内存地址相同,也就是说没有 copy。原来的 array 改变后,内存地址改变,但不影响被赋值的变量。

 */

输入: dividend = 10, divisor = 3
输出: 3

线程安全问题

以上的写法是不会报错的。如果把 array 的赋值写入 async closure,就会报错。多试几次,会有不同的错误。

 

示例 2:

Int array 的错误

DispatchQueue.global().async {
    let arr = my.intArr // 在这里赋值会报错
    var sum = 0
    for i in arr {
        sum += i
    }
    print("Sum:", sum)
}

澳门新葡亰手机版 4

澳门新葡亰手机版 5

澳门新葡亰手机版 6

class Person  {

输入: dividend = 7, divisor = -3
输出: -2

Struct array 的错误

DispatchQueue.global().async {
    let arr = my.structArr // 在这里赋值会报错
    var sum = 0
    for _ in arr {
        sum += 1
    }
    print("Sum:", sum)
}

澳门新葡亰手机版 7

澳门新葡亰手机版 8

    var _name: String = "HaRi"

说明:

Object array 的错误

DispatchQueue.global().async {
    let arr = my.objectArr // 在这里赋值会报错
    var sum = 0
    for _ in arr {
        sum += 1
    }
    print("Sum:", sum)
}

澳门新葡亰手机版 9

澳门新葡亰手机版 10

对于 Int array 和 struct array 来说,赋值时进行了 copy,但这个步骤应该不是原子操作,所以放入 async closure 会出错。对于 object array 来说,赋值过程虽然没有进行 copy,但是要改变原来的 array 并且保持被赋值的对象不变,应该也要进行 copy;也就是说在更新 array 时才进行 copy。推测此时的 copy 也不是原子操作,所以放入 async closure 会出错。

Array 的赋值过程是否进行 copy,与其中的元素类型有关。如果 array 的元素是 Int、struct 等,在赋值时就 copy。如果 array 的元素是 object,在赋值时不 copy,赋值后在更新其中一个 array 变量时才 copy。Array 的 copy 是线程不安全的。

转载请注明出处:

    var _age: Int = 26

  • 被除数和除数均为 32 位有符号整数。
  • 除数不为 0。
  • 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2**31,  2**31 − 1]。本题中,如果除法结果溢出,则返回 2**31 − 1。

    //实例方法一定是通过对象来调用的, 实例方法隶属于某一个类

 

    //如果不希望某个参数作为外部参数使用, 可以在参数前面加上 _ , 忽略外部参数

第一反应是这道题还是挺简单的,用减法实现除法不就好了,python刷题实现甚至可以直接使用range()来实现除法,需要注意的点如下:

    func setName(name: String, _ age: Int)

1.提前判断结果的正负号

    {

2.结果在[-2**31,2**31-1]中,要判断结果是否移除

        _name = name

3.使用range()来计算除法时,一旦除法可以整除我们要对结果+1,因为len(range(3,7,3))的结果是2,len(range(3,9,3))的结果也是2

        _age = age

 

    }

代码如下:

    func show()

class Solution(object):
    def divide(self, dividend, divisor):
        """
        :type dividend: int
        :type divisor: int
        :rtype: int
        """
        below = 1
        if dividend < 0 < divisor or divisor < 0 < dividend:
            below = -1

        dividend,  divisor = abs(dividend),  abs(divisor)
        if dividend < divisor:
            return 0
        elif divisor == 1:
            result = dividend * below
            if result >= 2**31-1:
                return 2**31-1
            return result


        result = len(range(divisor, dividend, divisor))
        if (result+1) * divisor == dividend:
            result += 1 
        return result * below

    {

自己写了好多case来测试都是没问题的,代码提交到leetcode,悲剧了。。。内存错误,看来是内存超了。问题出在核心语句len(range(divisor, dividend, divisor))上,怎么既能保证目前代码的简洁性又能降低内存使用呢。我解决办法是使用xrange代替range,简单的说range返回的对象是个list,会开辟一个很大的空间,而xrange不同,返回的是生成器,所以对内存的使用得到了直接的优化。重新提交,果然通过了。

        print("name = (_name) age = (_age)")

 

    }

最终代码如下:

}

class Solution(object):
    def divide(self, dividend, divisor):
        """
        :type dividend: int
        :type divisor: int
        :rtype: int
        """
        below = 1
        if dividend < 0 < divisor or divisor < 0 < dividend:
            below = -1

        dividend,  divisor = abs(dividend),  abs(divisor)
        if dividend < divisor:
            return 0
        elif divisor == 1:
            result = dividend * below
            if result >= 2**31-1:
                return 2**31-1
            return result


        result = len(xrange(divisor, dividend, divisor))
        if (result+1) * divisor == dividend:
            result += 1 
        return result * below

var p = Person()

 

p.show()

希望对大家有所帮助~

 

// 由于第一个参数可以通过方法名称指定, 所以默认第一个参数不作为外部参数

//p.setName(name:"xiaoHan", age:100)    Error!

p.setName(name: "hjq", 88)  //正确姿势

p.show()

 

//func setName(name:String, age:Int){

//func setName(name:String,myAge age:Int){

func setName(name: String, age: Int) {

    

}

// 实例方法和函数的区别在于, 实例方法会自动将除第一个参数以外的其他参数既当做为外部参数又当做内部参数, 而函数需要我们自己指定才会有外部参数, 默认没有

setName(name: "han", age: 30)

 

/*

 self关键字, Swift中的self和OC中的self基本一样; self指当前对象, self在对象方法中代表当前对象, 但是在类方法中没有self

 */

class Person2 {

    var name: String = "hjq"

    var age: Int = 25

本文由澳门新葡亰手机版发布于编程,转载请注明出处:的线程安全问题,两数相除

上一篇:函数类型,0服务端开发 下一篇:没有了
猜你喜欢
热门排行
精彩图文