与DispatchQueue进行快速数组同步

问题描述 投票:0回答:1

如果从同一个线程(线程1主线程)调用uploadFailed(for id: String)uploadSuccess()updateOnStart(_ id: String),我知道我们不需要同步队列。如果每次上载都从不同的线程调用函数,该怎么办。在哪里确保同步?它是上传状态还是状态,还是状态?

enum FlowState {
 case started(uploads: [String])
 case submitted
 case failed
}

class Session {
 var state: FlowState
 let syncQueue: DispatchQueue = .init(label: "Image Upload Sync Queue",
                                      qos: .userInitiated,
                                      attributes: [],
                                      autoreleaseFrequency: .workItem)

 init(state: FlowState) {
   self.state = state
 }

 mutating func updateOnStart(_ id: String) {
  guard case .started(var uploads) = state else {
    return
  }

  uploads.append(id)

  state = .started(uploads)
}

  mutating func uploadFailed(for id: String) {
   guard case .started(var uploads) = state else {
    return
   }

   uploads.removeAll { $0 == id }

   if uploads.isEmpty {
      state = .failed
   } else {
      state = .started(uploads)
   }
 }

 mutating func uploadSuccess() {
  state = .submitted
 }

}

我们是否同时同步uploads数组操作和状态,如下所示?

syncQueue.sync {
  uploads.append(id)

  state = .started(uploads)
}


syncQueue.sync {
  uploads.removeAll { $0 == id }

   if uploads.isEmpty {
      state = .failed
   } else {
      state = .started(uploads)
   }
}

OR

syncQueue.sync {
  state = .started(uploads)
}

syncQueue.sync {
  if uploads.isEmpty {
    state = .failed
  } else {
    state = .started(uploads)
  }
}

网络呼叫的完成处理程序可以更新Sessionstate属性。例如,用户选择10张图像并上传。完成后可能是失败或成功。对于我们上传的每个图像,我们缓存资源id,如果上传失败,则将其删除。当所有图像上传失败时,我们将更新状态.failed。我们只关心上传一张图片。上传一张图片后,我们会将状态更新为.submitted

ios swift synchronized dispatch-queue
1个回答
0
投票

使用同步来确保与FlowState的线程安全交互是完全有效的。

一些观察:

  1. 您提出了两种选择:

    syncQueue.sync {
        uploads.append(id)
    
        self.state = .started(uploads)
    }
    

    syncQueue.sync {
        state = .started(uploads)
    }
    

    这两个都不对。如果线程1和线程2同时调用此例程该怎么办。考虑:

    • 线程1检索uploads
    • 线程2检索相同的uploads;
    • 然后线程1进入其sync块,并将记录追加到其本地副本,保存它,并留下其sync闭包;
    • 然后线程2进入其sync块,并将其他记录追加到其自己的本地副本(没有线程1添加的记录),将其保存,并保留其sync闭包。您将丢失附加的线程1。

    因此,您需要采用第三种更广泛的同步机制,将检索,附加和存储uploads的整个过程包装在同步机制中:

    syncQueue.sync {
        guard case .started(var uploads) = self.state else {
            return
        }
    
        uploads.append(id)
    
        state = .started(uploads)
    }
    
  2. 您没有显示state的任何其他读数,但是如果有,请确保也同步您的读数。与state的所有交互都必须同步。

  3. 一个细微的问题:如果您要同步对state的访问,请确保将其设为private,以便没有外部代码可以访问它(否则可能会阻碍您尝试确保线程安全的交互)。您需要在同步机制中包装所有读取和写入。

    您可能也应该将同步队列设为私有,因为也无需公开该队列。

© www.soinside.com 2019 - 2024. All rights reserved.