Pergunta

Eu estou tentando trabalhar para fora em minha cabeça a melhor maneira de estruturar um aplicativo de cacau que é, essencialmente, um gerenciador de download simultâneo. Há um servidor as negociações APP para, o usuário faz uma grande lista de coisas para puxar para baixo, e os processos app que lista. (Não é usando HTTP ou FTP, então eu não posso usar o sistema de URL de carregamento;. Eu vou estar falando através de conexões de soquete)

Este é basicamente o padrão clássico produtor-consumidor. O truque é que o número de consumidores é fixo, e eles são persistentes. O servidor define um limite estrito do número de ligações simultâneas que pode ser aberta (mas geralmente, pelo menos, dois), e a abertura de novas ligações é caro, por isso, num mundo ideal, as mesmas conexões N são abertos para o tempo de vida do aplicativo.

Uma maneira de abordar esta poderia ser a de criar N fios, cada um dos quais seria "próprio" uma conexão, e espera na fila de solicitações, o bloqueio se ele está vazio. Como o número de conexões nunca será enorme, isso não é razoável em termos de sobrecarga do sistema actual. Mas conceitualmente, parece que Cacau deve oferecer uma solução mais elegante.

Parece que eu poderia usar um NSOperationQueue e setMaxConcurrentOperationCount: chamada com o número de conexões. Então eu simplesmente atirar os pedidos de download para essa fila. Mas eu não tenho certeza, nesse caso, a forma de gerir as próprias conexões. (Basta colocá-los em uma pilha, e confiar na fila para garantir que eu não over / under-run? Jogar em um expedição semáforo juntamente com a pilha?)

Agora que estamos no admirável mundo novo da o Grand Central Dispatch , faz que se abrem-se todas as outras formas de combater isso? À primeira vista, não parece como ele, desde que a habilidade carro-chefe da GCD para a simultaneidade escala dinamicamente (e mencionou em recomendações da Apple em Alterar produtor-consumidor Implementações ) realmente não me ajudar. Mas eu só arranhou a superfície de ler sobre isso.

EDIT:

No caso é importante: sim, eu estou pensando em usar as APIs de soquete assíncronas / non-blocking para fazer a comunicação real com o servidor. Assim, o I / O em si não tem que estar em seu próprio segmento (s). Eu só estou preocupado com a mecânica da fila do trabalho, e (com segurança) distribuindo-a para as ligações, assim que estiverem disponíveis.

Foi útil?

Solução 2

Para bem da posteridade, depois de alguma discussão em outros lugares, a solução que eu acho que eu iria adotar para isso é basicamente:

  • Ter uma fila de operações pendentes de download, inicialmente vazia.
  • Tenha um conjunto contendo todas as conexões abertas, inicialmente vazia.
  • Tenha uma matriz mutável (fila, na verdade) de conexões abertas ociosas, inicialmente vazia.
  • Quando o usuário adiciona um pedido de download:
    • Se a matriz de conexões ociosas não está vazio, remova um e atribuir o download para ele.
    • Se não houver conexões ociosas, mas o número de conexões totais não atingiu seu limite, abrir uma nova conexão, adicioná-lo ao conjunto, e atribuir o download para ele.
    • Caso contrário, enfileirar o download para mais tarde.
  • Quando um download for concluído: pedidos se não estão em fila, um dequeue e dai-à conexão; caso contrário, adicionar a ligação à lista de espera.

Todo esse trabalho teria lugar no segmento principal. O trabalho de decodificar os resultados de cada download seria transferida para GCD, para que ele possa lidar com a aceleração da concorrência, e não entupir o segmento principal.

Abrir uma nova conexão pode demorar um pouco, de modo que o processo de criação de um novo pode ser um pouco mais complicado na prática real (digamos, enfileirar o download, iniciar o processo de conexão, e depois dequeue quando a conexão é totalmente estabelecida). Mas eu ainda acho que a minha percepção da possibilidade de condições de corrida foi exagerada.

Outras dicas

Se você estiver usando do CFSocket não-bloqueio de chamadas para I / O, eu concordo, que todos devem acontecer no segmento principal, deixando a alça OS os problemas de concorrência, desde que você está apenas copiando dados e não realmente fazer qualquer computação.

Além disso, parece que o único outro trabalho às suas necessidades de aplicativos a fazer é manter uma fila de itens a serem baixados. Quando qualquer uma das transferências estiver concluída, a parte de trás chamada CFSocket pode iniciar a transferência do próximo item na fila. (Se a fila estiver vazia, diminuir sua contagem de conexão, e se algo é adicionado a uma fila vazia, iniciar uma nova transferência.) Eu não vejo por que você precisa vários segmentos para isso.

Talvez você tenha deixado de fora algo importante, mas com base na sua descrição do aplicativo é I / O limite, não vinculado à CPU, assim que todo o material de concorrência só vai tornar o código mais complicado com um impacto mínimo no desempenho.

Faça tudo no segmento principal.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top