Нет. Помещение контейнера в потокобезопасный контейнер не делает внутреннюю резьбу контейнера безопасной.
dictionary[user.GroupId].Add(user.Id.ToString());
вызывает добавление HashSet после извлечения его из ConcurrentDictionary. Если этот GroupId ищется из двух потоков одновременно, это нарушит ваш код со странными режимами сбоев. Я видел результат того, что один из моих товарищей по команде совершил ошибку, не заблокировав свои сеты, и это было не красиво.
Это правдоподобное решение. Я бы сделал что-то другое, но это ближе к вашему коду.
if (!dictionary.ContainsKey(user.GroupId)
{
dictionary.TryAdd(user.GroupId, new HashSet<string>());
}
var groups = dictionary[user.GroupId];
lock(groups)
{
groups.Add(user.Id.ToString())
}