The code has a bit more classes it needs to go through but the idea is the same.
CustomNetwork network;
network.to(torch::kCuda);
Game game;
while(game.NotFinished())
{
    int move = SelectMoveFromSearchTree();
    game.PlayMove(move);
    torch::Tensor value = network.forward(ConvertBoard(game.GetBoard());
    // do stuff with value
}

The above has the same gpu utilization as
CalculateGame(Game game, CustomNetwork network, int move);

CustomNetwork network[4];
network[0].to(torch::kCuda);
// Do this for all networks
Game game1;
Game game2;
Game game3;
Game game4;

int move[4] = Select4BestMoves(game1);

auto thread1 = std::thread(&CalculateGame, game1, network[0], move[0]);
auto thread2 = std::thread(&CalculateGame, game2, network[1], move[1]);
auto thread3 = std::thread(&CalculateGame, game3, network[2], move[2]);
auto thread4 = std::thread(&CalculateGame, game4, network[3], move[3]);

//join all threads

CalculateGame(Game game, CustomNetwork network, int move)
{
    while(game.NotFinished())
     {
         int move = SelectMoveFromSearchTree();
         game.PlayMove(move);
         torch::Tensor value = network.forward(ConvertBoard(game.GetBoard());
         // do stuff with value
     }
}

So I’m not sure if my CPU is bottlenecking here, because none of my 4 threads are waiting on other threads for data.