Два потока, использующие одну и ту же переменную, создают проблему - PullRequest
1 голос
/ 10 июля 2010

У меня есть список и два потока, которые используют список.

Первый поток получает новые подключения, каждое новое подключение добавляется в список.

Второй поток проходит по циклуСписок для обработки соединений (используя foreach).

Проблема заключается в том, что иногда, когда второй поток проходит по циклу по списку, список изменяется до завершения цикла.Я даже попытался создать новую копию списка и перебрать его.Но это вызывает некоторые другие проблемы.

Я не хочу создавать новый поток для обработки каждого нового соединения, потому что я понял, что слишком много потоков может снизить производительность.Есть ли другой способ обработки соединений?

Ответы [ 4 ]

3 голосов
/ 10 июля 2010

2 проблемы.

1) Вам необходимо заблокировать список.Вы должны убедиться, что у вас есть взаимный монопольный доступ к списку, чтобы его нельзя было перечислить во время его изменения.Первое решение состоит в использовании блокировок:

class Mailbox {
    List<int> list;

    void Send(int a) {
         lock(list) {
              list.Add(a);
         }
     }

     int Receive() {
         lock(list) {
             // Enumerate
             return ...;
         }
      }
}

Более элегантно, вы можете использовать одну из новых коллекций в пространстве имен Concurrent , например BlockingCollection .Последний не безопасен для перечисления, но предоставляет метод Take(), который можно использовать для извлечения объектов из него, пока производители вставляют их.

2) Избегайте создания потоков gazillion,Вы можете использовать .NET пул потоков , чтобы ставить в очередь столько запросов, сколько пожелаете, и фреймворк позаботится о том, чтобы отобразить их в реальных потоках, не убивая систему.

1 голос
/ 10 июля 2010

Как уже писали другие, вам нужно использовать блокировку для управления параллелизмом с этими двумя потоками.

Что касается избежания нескольких потоков, я думаю, что это во многом зависит от того, сколько потоков мы говорим. Если у вас есть только несколько соединений с сокетами, с которыми вы работаете, тогда, возможно, можно выполнять синхронное чтение каждого из них в отдельном потоке. Но если вы говорите о множестве сокетных подключений, я бы не стал использовать отдельный поток для каждого из них.

Наиболее эффективным способом чтения из нескольких сокетов является использование асинхронного чтения ( Socket.BeginReceive () ). Внутри этих асинхронных методов используются порты завершения ввода / вывода , которые очень эффективны. Относительно легко реализовать пользовательские TCP-серверы, которые могут обрабатывать многие тысячи одновременных соединений, используя асинхронные методы Socket.

1 голос
/ 10 июля 2010

Самое простое решение для этого - использовать lock в списке в каждой теме.

Первая тема:

lock(yourList)
{
    yourList.Add(...);
}

Вторая тема:

lock(yourList)
{
    foreach(var item in yourList)
    {
        ...
    }
}

Это предотвратит добавление соединения первым потоком, в то время как второй поток находится в середине итерации по нему.Если второй поток выполняет итерации по списку и первый пытается ввести секцию кода, * lock, он будет ждать, пока второй поток не завершит цикл по списку.

0 голосов
/ 10 июля 2010

Я действительно не знаю много об этом, но первая мысль, которая пришла мне в голову, была:

Сохраните длину списка и сделайте цикл

while (var i < lengthoflist)
{
//whatever fancy stuff your code does
i++;
}

Но я ничего не знаю о сокетах, это была просто общая идея, которую я придумал, поэтому я не знаю, сработает ли это.

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