2015-07-25 15 views
6

Trong thực hành nhanh chóng của tôi, tôi đã viết cấu trúc đơn giản có tên là OrderedSet.Swift 2: struct thread-safety

Tôi đã thử OrderedSet là một chuỗi an toàn với hàng đợi nối tiếp GCD.

Nhưng nó không hoạt động. Kết quả thử nghiệm không ổn định. Tôi mong đợi một cái gì đó như:

20:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 

nhưng không nhận được cái gì đó như như

2:[3, 19] 

đây là mã sân chơi:

import Foundation 
import XCPlayground 

struct OrderedSet<T: Equatable> { 
    mutating func append(e: T) { 
     dispatch_sync(q) { 
      if !self.__elements.contains(e) { 
       self.__elements.append(e) 
      } 
     } 
    } 
    var elements: [T] { 
     var elements: [T] = [] 
     dispatch_sync(q) { 
      elements = self.__elements 
     } 
     return elements 
    } 
    var count: Int { 
     var ret = 0 
     dispatch_sync(q) { 
      ret = self.__elements.count 
     } 
     return ret 
    } 
    private var __elements: [T] = [] 
    private let q = dispatch_queue_create("OrderedSet.private.serial.queue", DISPATCH_QUEUE_SERIAL) 
} 
extension OrderedSet: CustomStringConvertible { 
    var description: String { 
     var text = "" 
     dispatch_sync(q) { 
      text = "\(self.__elements.count):\(self.__elements)" 
     } 
     return text 
    } 
} 

// Test code 
let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 
let group = dispatch_group_create() 

var testSet = OrderedSet<Int>() 
for i in 0..<20 { 
    dispatch_group_async(group, globalQueue) { 
     testSet.append(i) 
    } 
} 
dispatch_group_notify(group, globalQueue) { 
    print("\(testSet)") // unstable result 
} 

XCPSetExecutionShouldContinueIndefinitely() 

Tôi đã kiểm tra dưới đây:

Đó là OK nếu định nghĩa OrderdSet là một lớp (không phải cấu trúc).

OK nếu sử dụng semaphore thay vì sử dụng hàng đợi nối tiếp.

Tôi muốn biết lý do tại sao cặp cấu trúc và hàng đợi nối tiếp không ổn định.

---- cập nhật

Tôi đã nhận được kết quả mong đợi.

  1. lớp thay vì struct

    import Foundation 
    import XCPlayground 
    
    class OrderedSet<T: Equatable> { 
        func append(e: T) { 
         dispatch_sync(q) { 
          if !self.__elements.contains(e) { 
           self.__elements.append(e) 
          } 
         } 
        } 
        var elements: [T] { 
         var elements: [T] = [] 
         dispatch_sync(q) { 
          elements = self.__elements 
         } 
         return elements 
        } 
        var count: Int { 
         var ret = 0 
         dispatch_sync(q) { 
          ret = self.__elements.count 
         } 
         return ret 
        } 
        private var __elements: [T] = [] 
        private let q = dispatch_queue_create("OrderedSet.private.serial.queue", DISPATCH_QUEUE_SERIAL) 
    } 
    extension OrderedSet: CustomStringConvertible { 
        var description: String { 
         var text = "" 
         dispatch_sync(q) { 
          text = "\(self.__elements.count):\(self.__elements)" 
         } 
         return text 
        } 
    } 
    
    // Test code 
    let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 
    let group = dispatch_group_create() 
    
    var testSet = OrderedSet<Int>() 
    for i in 0..<20 { 
        dispatch_group_async(group, globalQueue) { 
         testSet.append(i) 
        } 
    } 
    dispatch_group_notify(group, globalQueue) { 
        print("\(testSet)") // It's OK 
    } 
    
    XCPSetExecutionShouldContinueIndefinitely() 
    
  2. semaphore thay vì xếp hàng nối tiếp

    import Foundation 
    import XCPlayground 
    
    struct OrderedSet<T: Equatable> { 
        mutating func append(e: T) { 
         dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER) 
         if !self.__elements.contains(e) { 
          self.__elements.append(e) 
         } 
         dispatch_semaphore_signal(s) 
        } 
        var elements: [T] { 
         var elements: [T] = [] 
         dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER) 
         elements = self.__elements 
         dispatch_semaphore_signal(s) 
         return elements 
        } 
        var count: Int { 
         var ret = 0 
         dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER) 
         ret = self.__elements.count 
         dispatch_semaphore_signal(s) 
         return ret 
        } 
        private var __elements: [T] = [] 
        private let s = dispatch_semaphore_create(1) 
    } 
    extension OrderedSet: CustomStringConvertible { 
        var description: String { 
         var text = "" 
         dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER) 
         text = "\(self.__elements.count):\(self.__elements)" 
         dispatch_semaphore_signal(s) 
         return text 
        } 
    } 
    
    // Test code 
    let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 
    let group = dispatch_group_create() 
    
    var testSet = OrderedSet<Int>() 
    for i in 0..<20 { 
        dispatch_group_async(group, globalQueue) { 
         testSet.append(i) 
        } 
    } 
    dispatch_group_notify(group, globalQueue) { 
        print("\(testSet)") // It's OK 
    } 
    
    XCPSetExecutionShouldContinueIndefinitely() 
    
  3. đợi nối tiếp với OrderdSet riêng của mình.

    import Foundation 
    import XCPlayground 
    
    struct OrderedSet<T: Equatable> { 
        mutating func append(e: T) { 
         if !self.__elements.contains(e) { 
          self.__elements.append(e) 
         } 
        } 
        var elements: [T] { 
         return self.__elements 
        } 
        var count: Int { 
         return self.__elements.count 
        } 
        private var __elements: [T] = [] 
    } 
    extension OrderedSet: CustomStringConvertible { 
        var description: String { 
         return "\(self.__elements.count):\(self.__elements)" 
        } 
    } 
    
    // Test code 
    let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 
    let serialQueue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL) 
    
    let group = dispatch_group_create() 
    
    var testSet = OrderedSet<Int>() 
    for i in 0..<20 { 
        dispatch_group_async(group, globalQueue) { 
         dispatch_sync(serialQueue) { 
          testSet.append(i) 
         } 
        } 
    } 
    dispatch_group_notify(group, serialQueue) { 
        print("\(testSet)") // It's OK 
    } 
    
    XCPSetExecutionShouldContinueIndefinitely() 
    
+0

Vì vậy, triệu chứng là gì? [cách yêu cầu tràn ngăn xếp] (http://stackoverflow.com/help/how-to-ask) – rholmes

+0

kết quả mong đợi của tôi giống như "20: [0, 1, 2, 3, 4, 5, 6, 7 , 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] "nhưng kết quả giống như" 2: [3, 19] ". –

+0

@ tom.e.kid Vui lòng chia sẻ mã thử nghiệm cũng như – Kametrixom

Trả lời

3

Mã này sẽ chụp giá trị hiện tại của testSet:

dispatch_group_async(group, globalQueue) { 
    testSet.append(i) // `testSet` inside the closure will be a copy of the `testSet` variable outside 
} 

Sau khi thực hiện việc đóng cửa, giá trị của các bên testSet sẽ được sao chép vào testSet biến bên ngoài.

Hãy tưởng tượng một thế giới đồng thời:

  • 10 măng xông được chạy đồng thời, nắm bắt được giá trị ban đầu của bên ngoài testSet, đó là "0: []".

  • Sau khi hoàn tất, 10 bản sao của các đóng cửa bên trong testSet s cố gắng sao chép về chỉ bên ngoài testSet. Tuy nhiên, chỉ có một người chiến thắng, giả sử giá trị hiện tại của bên ngoài là testSet là "1: [3]".

  • Tuy nhiên, bắt đầu vòng khác, thu thập giá trị hiện tại của bên ngoài testSet là "1: [3]", thêm i và sao chép lại, cho kết quả lạ "2: [3, 19]"

Trong trường hợp của bạn được cập nhật 1, thay đổi OrderedSet đến lớp, mọi thứ đang khá thẳng về phía trước, testSet được chụp bằng cách tham chiếu, và tất cả các chủ đề được chia sẻ cùng một đối tượng.

Trong trường hợp cập nhật 3 của bạn, bằng cách sử dụng hàng đợi nối tiếp, tôi đoán mọi hoạt động chắp thêm và sao chép sẽ nối tiếp, vì vậy bạn mang lại một bộ đặt hàng hoàn hảo.

Trường hợp 2 phức tạp hơn. Trên thực tế tôi đã không tìm ra những gì đang xảy ra dưới mui xe. Và tôi nghĩ rằng đó là nhiều hơn về một chi tiết thực hiện của trình biên dịch nhanh chóng và có thể thay đổi trên các phiên bản nhanh khác nhau. Có vẻ như semaphore là một kiểu tham chiếu, do đó tất cả các bản sao của 'testSet's đang chia sẻ cùng một semaphore. Tôi đoán complier quyết định làm một số tối ưu hóa trong trường hợp này và làm cho tất cả các bản sao của testSet s ' điểm vào cùng một mảng. Vì vậy, kết quả chứa tất cả các yếu tố 0 .. < 20 nhưng thứ tự là không thể đoán trước.

0

Tôi nghĩ điều gì đang xảy ra khi dispatch_sync được sử dụng bên trong cấu trúc là self bị ẩn hoàn toàn bởi đóng cửa dưới dạng thông số inout.

Điều đó có nghĩa là một bản sao được sửa đổi, sau đó thay thế cho bên ngoài self khi trả lại. Vì vậy, có nhiều bản sao đồng thời của bản thân bị đột biến, sau đó ghi đè bản gốc.

Trong trường hợp semaphores không có đóng cửa vì vậy không có chụp để tự là tự là tự. Việc đột biến xảy ra trên bản thân bên ngoài ban đầu, và các semaphores đảm bảo rằng tất cả mọi người làm như vậy trong một dòng có trật tự.

Tôi đã chạy vào cùng một điều khi sử dụng bằng cách sử dụng một wrapper đóng cửa xung quanh mutexes pthread cho getters và setters bên trong một struct. Mặc dù thông số đóng là không thoát, cấu trúc (ví dụ: self) dường như vẫn được coi là một inout, vì vậy những điều lộn xộn xảy ra.

Các vấn đề liên quan