Thread safe progress counter

Viewed 176

I'm using the Parallel class for my tasks and need to implement a progress percentage counter. Tried combining different locks but either get very slow performance or the result is like on the screenshot.

Here is an example of code that works but slow. Because I'm blocking the whole part.

Parallel.ForEach(bossUsers, StandardParallelOptions(), (user) =>
{
    logger.Trace($"Sync new user {user.PID}.");
    while (queue.Count >= MaxDegreeOfParallelism)
    {
        Thread.Sleep(1);
    }

    queue.Enqueue(user);


    CreateOrUpdateUserAsync(
            cardRepository,
            dbScope,
            user,
            logger,
            cancellationToken)
        .ConfigureAwait(false);

    lock (locker)
    {
        var current = (int)(((progress) / (decimal)bossUsers.Count) * 100);
        if (current > 0 && current % 10 == 0)
        {
            if (last != current)
                logger.Info($"Progress: {current}%");
            last = current;
        }
    }

    logger.Trace($"Sync user {user.PID} complete.");
});

And if I remove the lock, I get this many similar lines

progress

Please advise how to make a lock so that it does not critically slow down the service. Thx!

1 Answers

So my quickest solution is to block only the assignment of the variable "last". I achieve the desired result without significant performance loss. But still closely watching the log I can see how the threads are slowing down.

I do not block calculations, but only assignment and output to the log if to retrieve the remainder.

var current = (int)((progress / (decimal)syncUsers.Count) * 100);

if (current > 0 && current % 10 == 0)
{
    lock (locker) // lock here only
    {
        if (last != current) logger.Info($"Complete: {current}%");
        last = current;
    }
}
Related