C# — тестирование параллелизма коллекции с помощью XUnit дает разные результатыC#

Место общения программистов C#
Anonymous
C# — тестирование параллелизма коллекции с помощью XUnit дает разные результаты

Сообщение Anonymous »

У меня есть параллельный класс под названием «Менеджер клиентов», который использует хэш-набор и ReadWriterLock для регистрации моих «клиентов» через их Guid:

Код: Выделить всё

internal class ClientManager : IClientManager
{
private readonly HashSet _clients = new HashSet();
private readonly ReaderWriterLock _lock = new ReaderWriterLock();

public IEnumerable Clients
{
get
{
try
{
_lock.AcquireReaderLock(100.Milliseconds());
var result = _clients.ToList();
return result;
}
finally
{
_lock.ReleaseReaderLock();
}
}
}

public void AddClient(Guid clientId, string login, string appName)
{
try
{
_lock.AcquireWriterLock(100.Milliseconds());
_clients.Add(clientId);
}
finally
{
_lock.ReleaseWriterLock();
}
}

public void RemoveClient(Guid clientId)
{
try
{
_lock.AcquireWriterLock(100.Milliseconds());
_clients.Remove(clientId);
}
finally
{
_lock.ReleaseWriterLock();
}
}
}
Я создал модульный тест, в котором используются три задачи: доступ к хеш-набору/добавление клиентов/удаление клиентов. Я ожидаю, что в конце модульного теста в хеш-наборе останется 0 записей:

Код: Выделить всё

    [Fact]
public void ClientManager_Should_Handle_Concurent_Operations()
{
var clientManager = new ClientManager();
var isDoneAdding = false;
var addTask = new Task(() =>
{
for (var i = 0; i < 10000; i++)
{
//Adding client should enter write lock
clientManager.AddClient(Guid.NewGuid(), $"login_{i}", $"appName_{i}");
}

isDoneAdding = true;
});

var accessTask = new Task(() =>
{
for (var i = 0; i < 10000; i++)
{
//Getting clients should enter read lock
var clients = clientManager.Clients;
}
});

var removeTask = new Task(() =>
{

for (var i = 0; i < 10000; i++)
{
var existingGuid = clientManager.Clients.FirstOrDefault();
if (existingGuid != default)
{
//Removing client should enter write lock
clientManager.RemoveClient(existingGuid);
}
}

//refactor that
while (clientManager.Clients.Count() > 0 && isDoneAdding)
{
var existingGuid = clientManager.Clients.FirstOrDefault();
if (existingGuid != default)
{
clientManager.RemoveClient(existingGuid);
}
}
});

accessTask.Start();
addTask.Start();
removeTask.Start();

Task.WaitAll(accessTask, addTask, removeTask);
Assert.True(clientManager.Clients.Count() == 0);
}
Модульный тест всегда завершается неудачно:
Неудачный тест
Я изменил хэш-набор и систему блокировки для параллельного словаря следующим образом:
р>

Код: Выделить всё

internal class ClientManager : IClientManager
{
//We don't need the value here so just put a byte to reduce memory footprint
private readonly ConcurrentDictionary _clients = new ConcurrentDictionary();
private readonly ReaderWriterLock _lock = new ReaderWriterLock();

public IEnumerable Clients
{
get
{
return _clients.Keys;
}
}

public void AddClient(Guid clientId, string login, string appName)
{
_clients.TryAdd(clientId, default);
}

public void RemoveClient(Guid clientId)
{
_clients.Remove(clientId, out _);
}
}
Юнит-тест прошёл успешно 320 раз подряд: успешный тест
Я не понимаю, чего мне не хватает в системе блокировки хешсета это работает, может кто-нибудь помочь мне понять, что происходит?
Спасибо за чтение!

Подробнее здесь: https://stackoverflow.com/questions/786 ... erent-resu

Вернуться в «C#»