Implementando 'apenas um de' e 'não em paralelo' semântica com a tarefa Biblioteca paralela

StackOverflow https://stackoverflow.com/questions/3613812

  •  26-09-2019
  •  | 
  •  

Pergunta

Qual seria a melhor abordagem para implementar tarefas com uma chave que opera da seguinte maneira:-

Opção 1) Apenas uma dessa chave está sempre pendente. Pode ser usado, por exemplo, do ASP.NET MVC para fazer fila uma única renderização para uma imagem de miniatura, não importa quantas vezes o URL da imagem seja atingido. Apenas uma execra, todos os outros pedidos esperam que este seja concluído.

Opção 2) Todos os itens com a mesma chave devem ser executados sequencialmente. Pode ser usado, por exemplo, para garantir que as operações que busquem um arquivo de uma loja de apoio a um cache local nem todas tentem colocar o arquivo no cache ao mesmo tempo. A opção 1 é um caso especial disso, onde as ações subsequentes com a mesma chave são simplesmente descartadas (normalmente salva apenas um arquivo-existe-verificação).

Eu tenho uma câmara de trabalho existente que lida com esses dois casos (assim como o estado do apartamento, as configurações da prioridade do thread e os graus máximos de paralelismo). O TPL parece ser a melhor solução para substituir isso e trará opções de cancelamento aprimoradas.

As tarefas aninhadas com continuações parecem esperançosas, mas manter um dicionário de tarefas atualmente na fila logo fica confuso entre as classes de tarefas e tarefas. A herdeira da tarefa também é problemática, pois nem a TaskFactory nem o TaskScheduler são genéricos na tarefa.

A maioria dos exemplos paralelos de tarefas assume que o conjunto de tarefas é conhecido com antecedência. Nesse caso, novas tarefas são adicionadas o tempo todo e precisam ser descartadas ou acorrentadas às tarefas existentes, dependendo da operação solicitada e da chave passada.

Alguém implementou algo semelhante a isso usando o TPL e, em caso afirmativo, que abordagem você adotou em sua tarefa, TaskScheduler e TaskFactory Classes?

Foi útil?

Solução

Talvez, uma maneira que eu possa pensar é

  1. Crie uma classe de wrapper - digamos KeyProcessor para fazer fila de itens para uma chave.
  2. O método keyprocessor.run () será capaz de qualquer semântica de fila que você precise. Essencialmente, ele procuraria uma fila interna para qualquer trabalho pendente e, em seguida, continuaria fazendo isso sequencialmente.
  3. Mantenha o dicionário dos objetos do processador de chaves.
  4. Para qualquer nova tarefa, verifique o dicionário para obter a mesma chave. Se não existir, adicione -o. Fila a tarefa nela. Se não estiver em execução, agende -o com TPL usando o método Run como ação.
  5. Use Continuewith para agendar a tarefa do mantenedor - por exemplo, sempre que a tarefa de execução de keyprocessor.run é concluída, as tarefas de continuação podem verificar se há mais tarefas agendadas para a mesma chave (uma vez que foi concluída) e iniciá -la novamente ou remover do dicionário.

Tudo acima teria sido complicado do ponto de sincronização do thread não para poucas coleções interessantes presentes em System.Collection.Concurrent espaço para nome. Isso tornaria a lógica acima muito mais simples. Por exemplo, ConcurrentDictionary.getoradd permitirá procurar e/ou adicionar o objeto KeyProcessor de maneira segura por threads.

Outras dicas

Este problema é semelhante ao que eu resolvi Reactivexaml, embora o meu também tenha memorado solicitações anteriores. Dê uma olhada no código para Feedasyncmrucache (e os seus entrada no blog) - Este código combina o TPL com as extensões reativas para fazer esse tipo de coisa, mas garante importante que a segunda solicitação para a mesma chave bloqueará a primeira solicitação de voo em vez de emitir outra.

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