Как заморозить коллекцию, чтобы можно было ее перебирать? - PullRequest
0 голосов
/ 06 июня 2018

Следующий код дает мне исключение:

var snapshot = BluetoothCapture.Instance.Snapshot();
var allowedDevice = snapshot.FirstOrDefault( _ => some_expression );

Коллекция была изменена;Операция перечисления может не выполняться.

Я думал, что мог бы использовать lock , чтобы заморозить коллекцию, чтобы я мог выполнить ее итерацию.Тем не менее, я все еще получаю то же исключение.

В приведенном ниже определении класса есть метод Snapshot , который пытается это:

public partial class BluetoothCapture
{
    ...

    public void Capture()
    {
        _watcher = DeviceInformation.CreateWatcher();
        _watcher.Added += (s, e) => { _devices.Add(e); };
        _watcher.Start();
    }

    public IEnumerable<DeviceInformation> Snapshot()
    {
        lock (_devices)
        {
            return _devices.AsReadOnly();
        }
    }
}

Есть предложения?

Ответы [ 2 ]

0 голосов
/ 06 июня 2018

lock на самом деле очень полезная концепция, но только при разумном ее использовании.

Если в дальнейшем коде вы не хотите обновлять ссылку на snapshot (коллекция, которую вы получаете отBluetoothCapture.Instance.Snapshot()), но просто выполните запрос Linq, чтобы получить отфильтрованное значение для выполнения некоторой логики. вы можете избежать использования lock.

Это также будет полезно, так как не выполняя lock, вы фактически не удерживаете другие потоки для выполнения его логики.- и мы не должны игнорировать тот факт, что ужасное использование lock может вызвать серьезные проблемы, такие как dead-lock.

вы получаете это исключение, наиболее вероятно, коллекцию, в которой вы выполняете linqзапрос;обновляется другим потоком.(у меня тоже возникла эта проблема).

вы можете сделать одну вещь, вместо того, чтобы использовать общую ссылку на коллекцию (ту, которую вы получаете от BluetoothCapture.Instance.Snapshot()), вы можете создать локальный список - так как он локальныйон не будет обновляться другими потоками.

0 голосов
/ 06 июня 2018

Блокировка используется, когда вам нужно остановить блок кода для выполнения в нескольких потоках (остановить параллельное выполнение).Если Capture вызывается несколько раз, так что да, вы можете вызвать запись до того, как завершится предыдущая.

Вы можете использовать ConcurrentBag.ConcurrentBag - это объект, подобный списку, но он безопасен для потоков (ни один из общих списков не является).Но ConcurrentBag - неупорядоченная коллекция, поэтому она не гарантирует порядок.

Если вам нужен какой-то упорядоченный список, вы можете увидеть эту ссылку Потокобезопасные коллекции в .NET

Вы также можете сделать «блокировку» в Добавить (не вget)

 _watcher.Added += (s, e) => { lock(_devices){_devices.Add(e); }};

Но, если ваше приложение работает некоторое время, у вас могут возникнуть проблемы с памятью и производительностью (добавление не будет асинхронным), даже если Capture имеет значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...