2012-06-07 8 views
3
public void GatherDataFromSwitches(Device[] switches) 
{ 
    List<Thread> workerThreads = new List<Thread>(); 
    for(int i = 0; i < switches.Length - 1; i++) 
    { 
     Thread t = new Thread(unused => GatherDataFromSwitch(switches[i])); 
     workerThreads.Add(t); 
     t.Start(); 
    } 
    foreach (Thread d in workerThreads) d.Join(); //wait for all threads to finish 
} 

Если я пройду через коммутаторы после запуска этого метода, я заметил, что каким-то образом некоторые коммутаторы не добавили данных, а некоторые коммутаторы имели данные, добавленные с нескольких коммутаторов. Так что что-то пошло не так с передачей ссылки на рабочие потоки. Я до сих пор не уверен, что именно, но я решил эту проблему, добавивC# - Создание нескольких потоков с использованием Lambda

Thread.Sleep(100); 

сразу после

t.Start(); 

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

Как правильно обойти это?

ответ

3

Проблема заключается в том, как i зафиксирован в лямбда. Сделать локальную копию внутри цикла, чтобы каждый Capture Лямбда определенное значение:

public void GatherDataFromSwitches(Device[] switches) 
{  
    List<Thread> workerThreads = new List<Thread>(); 
    for(int i = 0; i < switches.Length ; i++) 
    { 
     int j = i; // local i 
     Thread t = new Thread(unused => GatherDataFromSwitch(switches[j])); 
     workerThreads.Add(t); 
     t.Start(); 
    } 
    foreach (Thread d in workerThreads) d.Join(); //wait for all threads to finish 
} 

Или передать i явно в качестве параметра резьбы:

public void GatherDataFromSwitches(Device[] switches) 
{  
    List<Thread> workerThreads = new List<Thread>(); 
    for(int i = 0; i < switches.Length ; i++) 
    { 
     Thread t = new Thread(param => { j = (int)param; GatherDataFromSwitch(switches[j]); }); 
     workerThreads.Add(t); 
     t.Start(i); 
    } 
    foreach (Thread d in workerThreads) d.Join(); //wait for all threads to finish 
} 
+0

Это действительно исправляет его, я первоначально начал с цикла foreach. Кажется странным копировать объект, чтобы передать его лямбда. –

+0

@Maximiliaan Aelvoet: На самом деле, я предпочитаю вторую версию, которая делает более понятным, что вы передаете индекс в качестве параметра в поток. По крайней мере, для меня это более понятно, так как я начал программировать с помощью pthreads. :) Кстати, не забудьте отметить ответ, как принято, если он решит вашу проблему. – Tudor

+0

Просто для объяснения: проблема с исходной версией заключалась в том, что lambda принимает значение 'i' в момент его фактического выполнения. Поскольку потоки, которые вы запускаете, запускаются асинхронно, нет способа узнать, какое значение будет иметь значение 'i', к тому времени, когда каждый поток достигнет кода, который использует' i'. – Tudor

0
public void GatherDataFromSwitches(Device[] switches) { 
    List<Thread> workerThreads = new List<Thread>(); 
    for(int i = 0; i < switches.Length ; i++) 
    { 
     int j = i; 
     Thread t = new Thread(unused => GatherDataFromSwitch(switches[j])); 
     workerThreads.Add(t); 
     t.Start(); 
    } 
    foreach (Thread d in workerThreads) d.Join(); //wait for all threads to finish 
} 

запиской копирование i в j. Если у вас был resharper, ваш код будет выдавать предупреждение, больше об этом в вопросе Access to Modified Closure

Смежные вопросы