2012-04-24 2 views
3

Я играл вокруг с следующим фрагментом кода:Предоставляет ли ParameterizedThreadStart, чтобы экземпляр объекта не собирался собирать мусор?

class RunMeBaby 
{ 
    public void Start() 
    { 
     while (true) 
     { 
      Console.WriteLine("I'm " + Thread.CurrentThread.ManagedThreadId); 
      Thread.Sleep(1000); 
     } 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     RunMeBaby r = new RunMeBaby(); 
     Thread t = new Thread(r.Start); // ParameterizedThreadStart delegate 
     r = null; 
     GC.Collect(GC.MaxGeneration); 
     t.Start(); 

     r = new RunMeBaby(); 
     t = new Thread(() => r.Start()); // ThreadStart delegate 
     t.Start(); 
     //Thread.Sleep(1000); 
     r = null; 
    } 
} 

В то время как первая часть основным выполнена без сучка, то вторая часть терпит неудачу, когда я закомментировать вызов метод Thread.Sleep(), Я получаю пустое исключение.

Мое понимание заключается в том, что выражение лямбда лениво оценивается, может случиться так, что новая нить не запускается достаточно быстро, а основная устанавливает r на null. Теперь я поставил эту «вторую часть» в статическом методе с r с локальной областью, и проблема исчезла. Но я задаюсь вопросом, скрыта ли проблема планировщиком потоков в этом конкретном случае, может быть, на другой машине с другой рабочей нагрузкой, которая все еще может произойти. Или есть что-то о выражении лямбда, которое гарантирует, что хотя r выпадает из области видимости, если он не был установлен в null, он все равно упоминается.

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

ответ

5

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

Существует огромная разница между:

new Thread(r.Start) 

который создает делегат для Start метода на текущем значении r, т.е.

new Thread(new ThreadStart(r.Start)) // identical to new Thread(r.Start) 

ни в одном из указанных выше, r оценивается сейчас, поэтому последующие изменения в другой экземпляр (или null) не повлияют на него. Контраст с:

new Thread(() => r.Start()) 

это анонимный метод с захватами переменной r, т.е. r только оценивали , когда анонимный метод вызывается, то есть, когда второй поток выполняется. Следовательно, да: если вы измените значение r, вы можете очень хорошо получить другой результат (или ошибку, если вы измените его на null).

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

new Thread(state => ((RunMeBaby)state).Start(), r); 

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

Теперь! Что касается сбора мусора, то ничего особенного не знать. Да, передав ссылку r в ParameterizedThreadStart, вы создадите копию ссылки (а не копию объекта), поэтому предотвратит сбор мусора, пока она больше не будет в сфере видимости. То же самое, однако, относится и к исходному подходу new Thread(r.Start). Единственный момент, когда это становится сложным, - это пример «захваченной переменной» (() => r.Start()), хотя проблема, которую вы видите, имеет ничего общего, чтобы сделать с GC, и все, что связано с правилами захваченных переменных.

2

Множество вопросов, о которых я все не понимаю. Что я могу сказать, что

Thread t = new Thread(r.Start) 

создает делегат, который будет внутренне указать на экземпляре, который обеспечивает реализацию метода, которая будет вызвана через делегата. Следовательно, установка r в null будет недействительной, поскольку существует связь Thread -> ParameterizedDelegate -> ваш метод -> RunMeBaby.

r = new RunMeBaby(); 
t = new Thread(() => r.Start()); 

создает замыкание вокруг r, которое по-прежнему позволит вам изменить r. Если вы установите для него значение null и получите к нему более позднюю точку, вы получите NRE.

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