🎮 Singleton e Concorrência em C# — Um Joguinho pra Aprender de Verdade
Rodrigo Schemes

Rodrigo Schemes @rodrigoschemes

About: Software Developer. Trabalho com Asp.Net Core e gosto de assuntos relacionados a Extreme Programming e Arquitetura de Software. Moro em São Caetano do Sul, São Paulo.

Location:
São Caetano do Sul, São Paulo, Brasil
Joined:
Sep 5, 2023

🎮 Singleton e Concorrência em C# — Um Joguinho pra Aprender de Verdade

Publish Date: Jun 10
0 3

Fiz esse exemplo pra entender melhor como o padrão Singleton funciona na prática e como lidar com concorrência em C#. A ideia foi criar uma simulação de um jogo onde vários jogadores disputam uma pontuação limitada. No caminho, explorei técnicas de sincronização e execução paralela.


🔄 O que é Singleton, afinal?

O Singleton é um padrão de projeto que garante que uma classe tenha apenas uma instância durante toda a execução do programa.

Por que usar isso?

  • É útil quando precisamos de um ponto centralizado de controle, como um serviço de log, um gerenciador de configuração ou — no nosso caso — uma pontuação global.
  • Evita instanciar objetos repetidamente sem necessidade.
  • Garante que todo mundo (todas as threads, inclusive) estejam olhando pro mesmo objeto.

No meu código usei Lazy<T> pra deixar isso thread-safe e garantir que a instância só seja criada quando for realmente usada.

public sealed class PlacarGlobal
{
    private static readonly Lazy<GlobalScore> _instancia = new(() => new PlacarGlobal());
    public static PlacarGlobal Instancia => _instancia.Value;

    private PlacarGlobal() { }
}
Enter fullscreen mode Exit fullscreen mode

🕹️ O jogo: como funciona?

  • Temos 500 pontos no total.
  • 5 jogadores jogam ao mesmo tempo, cada um numa thread (ou Task).
  • A cada jogada, o jogador tenta pegar entre 1 e 10 pontos aleatoriamente.
  • Quando os pontos acabam, o jogo termina e mostramos o ranking final.

⚙️ Como fiz funcionar direitinho (sem bugs de concorrência)

1. lock para proteger a pontuação

Como todos os jogadores acessam e alteram os mesmos dados (pontuação total e placar), usei um lock pra evitar bagunça:

lock (_lock)
{
    // Acesso seguro
}
Enter fullscreen mode Exit fullscreen mode

Isso garante que só uma thread por vez consiga pegar pontos.

2. Um cuidado com o Random

Se você usar new Random() direto em várias threads, pode acabar com números repetidos. Resolvi isso gerando a semente com Guid.NewGuid().GetHashCode(). Assim cada thread tem um gerador único:

var rng = new Random(Guid.NewGuid().GetHashCode());
Enter fullscreen mode Exit fullscreen mode

3. Execução concorrente com Task.Run

Cada jogador roda numa Task, e eu espero todas terminarem com Task.WhenAll. Simples e eficiente.

var tasks = new List<Task>();

for (var i = 1; i <= 5; i++)
{
    var jogadorId = i;
    tasks.Add(Task.Run(async () => {
        while (ObterPontos(jogadorId))
            await Task.Delay(rng.Next(100, 500));
    }));
}

await Task.WhenAll(tasks);
Enter fullscreen mode Exit fullscreen mode

4. Ranking final com ConcurrentDictionary

Usei um ConcurrentDictionary pra armazenar o placar de cada jogador. Ele já é thread-safe, então não precisei de mais lock pra isso.


🏁 Resultado típico

### Iniciando partida! ###
### Jogador 1 pegou 8 pontos. Restantes: 492 ###
...
### Jogador 3 terminou. ###

### Ranking Final: ###
#1 - Jogador 4 : 142 pontos
#2 - Jogador 2 : 129 pontos
#3 - Jogador 1 : 113 pontos
#4 - Jogador 5 : 74 pontos
#5 - Jogador 3 : 42 pontos

### Pontos distribuídos: 500/500 ###
Enter fullscreen mode Exit fullscreen mode

💭 O que eu aprendi com isso

  • Singleton com Lazy<T> é super simples e seguro.
  • Concorrência em C# não precisa ser um bicho de sete cabeças se você usar lock e Task com cuidado.
  • E dá pra se divertir criando um joguinho simples e ver os conceitos funcionando na prática.

Se quiser, dá pra adaptar esse mesmo código pra outras situações onde vários processos disputam um recurso limitado.


🔗 Código-fonte no GitHub

Todo o código apresentado neste artigo está disponível no repositório:

👉 github.com/rodrigo-schemes/csharp-designpatterns

Sinta-se à vontade para clonar, estudar e até contribuir com sugestões ou melhorias. Esse projeto faz parte de uma série voltada para estudo e prática de Design Patterns com C#.

Comments 3 total

Add comment