2010-07-10 4 views
0

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

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

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

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

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

+3

Если вы учитесь делать потоки, не верьте другим людям, когда говорят, что «слишком много потоков может повредить производительность». Сделайте это сами и узнайте, каковы ограничения в вашей ситуации. Вы узнаете гораздо больше. –

+0

Какие проблемы возникают при копировании списка? – SwDevMan81

+0

В нем говорится, что целевой массив не имеет нужного размера (массив изменяется до завершения копирования). –

ответ

3

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 thread pool, чтобы залить столько запросов, сколько захотите, и структура позаботится о том, чтобы сопоставить их с фактическими потоками, не убивая систему.

+0

'BlockingCollection' только синхронизирует * изменения * в списке. Итерация по-прежнему требует явного блокировки. –

+0

Да, вы правы, перечисление должно быть устранено, и вместо этого можно использовать 'Take()'. – Mau

-4

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

магазин длина списка и сделать петлю идти

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

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

+0

Не работает (безопасно). Вероятно, он не будет бросать, но он может пропустить предметы или что-то подобное. –

1

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

Первый поток:

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

Второй поток:

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

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

+0

@Jouke: Да, это то, что я сказал в конце ответа. Это то, что звучало так, как просил ОП. –

1

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

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

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