AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 78610314
Accepted
bli00
bli00
Asked: 2024-06-12 09:36:04 +0800 CST2024-06-12 09:36:04 +0800 CST 2024-06-12 09:36:04 +0800 CST

Segurança de simultaneidade durante atualizações de frames no Swift

  • 772

Eu tenho um método chamado renderque é chamado em cada quadro para desenhar algo na tela usando Metal. Ele está renderizando uma série de pontos armazenados dentro da classe. No entanto, ocasionalmente preciso atualizar esse conjunto de pontos. Estou lutando para encontrar uma solução que possa atualizar esses pontos de maneira segura para a simultaneidade.

Ou seja, ao rendertentar acessar esse array, outro thread pode estar atualizando-o, causando uma falha. Além disso, renderé síncrono e não pode bloquear ou aguardar a atualização. Este parece ser um problema de simultaneidade comum, por isso estou procurando ajuda aqui para ver quais soluções existem.

Para generalizar o problema - há alguma função síncrona que é chamada com muita frequência, ela precisa de acesso a um dado que pode ser atualizado por outro thread. Eu tenho controle sobre o rendermétodo e o "outro" thread que faz a atualização. Como posso evitar que uma condição de corrida trave o aplicativo? (só não precisa travar, não há considerações de "correção" dos dados)


Uma possível solução que eu hipotetizei é usar atômicos. Não tenho certeza de como isso poderia ser feito, especialmente em Swift (não acha que Swift tem atômicos?)


EDITAR:

Tentarei adicionar mais contexto aqui, mas pode ser difícil adicionar um MRE, considerando que parte do que estou implementando está guardado em um repositório privado ao qual não tenho acesso.

Resumindo, estou tentando implementar uma camada personalizada do MapBox seguindo este exemplo . Os detalhes desta renderfunção específica no exemplo são irrelevantes, pois meu método de renderização precisa fazer algo completamente diferente, mas seria algo assim em pseudocódigo:

class MyCustomLayer: NSObject, CustomLayerHost {
    var locationData: [CLLocation] = []
    
    func render(...) {
        // calculate the viewport from the parameters
        let viewport = ...
        if viewport.fitSomeCondition {
            // fetch new location data. This can't block and needs to happen async
            fetchLocationDataFromServer(viewport: viewport)
            // need to somehow update the locationData
        }
        
        // render locationData
    }
}

Aqui locationDataestá uma matriz de longitudes e latitudes e renderestá separada do CustomLayerHostprotocolo do MapBox iOS SDK . O código-fonte de como CustomLayerHosté tratado não está disponível ao público, mas aqui está a declaração do cabeçalho:

/**
 * Render the layer. This method is called once per frame.
 *
 * This method is called if underlying map rendering backend uses Metal graphic API.
 *
 * @param The `parameters` that define the current camera position.
 * @param The MTLCommandBuffer used to render all map layers.
 * Use to create new command encoders. Do not submit as there could be map
 * layers following this custom layer in style's layer list and those won't get
 * to be encoded.
 * @param The MTLRenderPassDescriptor that defines rendering map to view drawable.
 *
 */
- (void)render:(nonnull MBMCustomLayerRenderParameters *)parameters
mtlCommandBuffer:(nonnull id<MTLCommandBuffer>)mtlCommandBuffer
mtlRenderPassDescriptor:(nonnull MTLRenderPassDescriptor *)mtlRenderPassDescriptor;

Entendo que este não é um MRE, mas espero que ajude a tornar a questão mais clara.

swift
  • 1 1 respostas
  • 40 Views

1 respostas

  • Voted
  1. Best Answer
    Rob
    2024-06-12T15:25:03+08:002024-06-12T15:25:03+08:00

    Se você está apenas tentando evitar a corrida de dados locationData, recomendo adicionar alguma sincronização. Por exemplo, em cenários de alto desempenho, podemos optar por uma sincronização baseada em bloqueio:

    import os.lock
    
    class MyCustomLayer: NSObject, CustomLayerHost {
        private let lock = OSAllocatedUnfairLock()    // or `NSLock` would likely be fine, too
        private var _locationData: [CLLocation] = []
        
        // synchronized access to _locationData
        
        private var locationData: [CLLocation] {
            get { lock.withLock { _locationData } }
            set { lock.withLock { _locationData = newValue } }
        }
    
        func render() {
            // calculate the viewport from the parameters
            let viewport = …
    
            // fetch locations if necessary
            if viewport.fitSomeCondition {
                fetchLocationDataFromServer()
            }
    
            // in the meantime, fetch copy of `locationData`
            let locationsToRender = locationData
    
            // now render `locationsToRender`, our local copy
            …
        }
        
        func fetchLocationDataFromServer() {
            let request: URLRequest = …
            let task = URLSession.shared.dataTask(with: request) { [self] data, response, error in
                locationData = …
            }
            task.resume()
        }
    }
    

    Existem muitos padrões de sincronização (filas seriais GCD, atores, etc.), mas para algo que precisa ser executado tão frequentemente quanto quadro a quadro, eu poderia me inclinar para um mecanismo de sincronização com melhor desempenho, ou seja, bloqueios injustos, mostrados acima .

    Observe que queremos minimizar o número de sincronizações, portanto, eu evitaria referenciar locationDatarepetidamente dentro de render, mas sim fazê-lo uma vez, atribuindo-o a uma variável local.

    Além disso, está além do escopo da questão, mas pense muito fitSomeConditione evite solicitações de rede redundantes ou simultâneas.


    Se valer a pena, posso considerar mover essa sincronização baseada em bloqueio para seu próprio wrapper de propriedade:

    @propertyWrapper
    class Synchronized<T> {
        private let lock = OSAllocatedUnfairLock()
        private var _wrappedValue: T
        
        var wrappedValue: T {
            get { lock.withLock { _wrappedValue } }
            set { lock.withLock { _wrappedValue = newValue } }
        }
        
        init(wrappedValue: T) {
            _wrappedValue = wrappedValue
        }
    }
    

    E isso simplifica MyCustomLayer:

    class MyCustomLayer: NSObject, CustomLayerHost {
        @Synchronized private var locationData: [CLLocation] = []
        
        func render() {
            let viewport = …
    
            if viewport.fitSomeCondition {
                fetchLocationDataFromServer()
            }
    
            // fetch copy of `locationData`
            let locationsToRender = locationData
    
            // now render `locationsToRender`, our local copy
            …
        }
        
        func fetchLocationDataFromServer() {
            let request: URLRequest = …
            let task = URLSession.shared.dataTask(with: request) { [self] data, response, error in
                locationData = …
            }
            task.resume()
        }
    }
    
    • 1

relate perguntas

  • IOS (simulador) --> Local Vapor POST Image/png: Abort.413: Payload Too Large

  • Redimensione a imagem antes de salvar no Core Data

  • Como lidar com dois manipuladores de conclusão em uma função swift

  • Por que um dicionário de chave e valores codificáveis ​​não é codificável?

  • Existe uma maneira de incorporar um assertionFailure em '?' expressão

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve