2009-11-04 2 views
1

по теме: How to catch exceptions from a ThreadPool.QueueUserWorkItem?Один читателя, многие писатели

Я ловить исключения в фоновых потоках, созданные ThreadPool.QueueUserWorkItem() и распространяющиеся их в основной поток через общий переменный экземпляр.

Фоновые потоки сделать это:

try 
{ 
    ... stuff happens here... 
} 
catch (Exception ex1) 
{ 
    lock(eLock) 
    { 
     // record only the first exception 
     if (_pendingException == null) 
      _pendingException = ex1; 
    } 
} 

Есть несколько потенциальных авторов к _pendingException - множественные фоновые потоки - так что я защитить его с замком.

В основной теме я должен взять замок перед чтением _pendingException? Или я могу просто сделать это:

if (_pendingException != null) 
    ThrowOrHandle(); 

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

+0

Ваше описание zdnet не так ли? – Spence

+0

Вы делаете что-нибудь, за исключением того, что прошло? Или вы могли бы использовать bool, чтобы отметить, что произошла ошибка? –

+0

zdnet? Нет.Я даже не знаю, что это. Умм, арендатор ... я использую исключение ... ну да. Я реконструирую его из основного потока. – Cheeso

ответ

1

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

  1. В многоядерных ЦП без изменения переменной энергозависимости (или выполнения любой операции с защитой памяти) может возникнуть момент, когда потоки, запущенные на разных ядрах, могут видеть разные значения. (я не уверен, что вызов lock(queue) в рабочем потоке вызовет любую операцию барьера памяти). (обновление) Вызов lock(queue) в рабочем потоке вызовет операцию барьера памяти, как указано Эриком в комментарии ниже.

2. Пожалуйста, имейте это в голову, что References are not addresses (Эрик Липперт) (если вы при условии ссылки на 32-битные адреса в 32-разрядной среде CLR, которые могут быть прочитаны атомарно). Реализация ссылок может быть изменена на некоторые непрозрачные структуры, которые могут не читаться атомарно в будущей версии CLR (хотя я думаю, что это вряд ли произойдет в обозримом будущем :)), и ваш код сломается.

+2

Это НЕ ПРАВИЛЬНО. В спецификации четко указано, что чтение и запись ссылок должны быть атомарными во всех реализациях C#. Подробности см. В разделе 5.5 спецификации. –

+0

Обновлена ​​(вычеркнута) неверная информация, указанная Эриком Липпертом. Спасибо за исправление и прошу прощения за этот поздний ответ :). Если номер 1 все еще некорректен, сообщите мне. Опять же, спасибо! –

+1

Что касается вашего первого момента: блокировка вызывает барьер памяти. –

3

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

 
try 
{ 
    ... stuff happens here... 
} 
catch (Exception ex1) 
{ 
    lock(queue) 
    { 
     queue.Enqueue(ex1); 
     Monitor.PulseAll(queue); 
    } 
} 

И обработать его:

 

while(!stopped) 
    lock (queue) 
    { 
     while (queue.Count > 0) 
      processException(queue.Dequeue()); 
     Monitor.Wait(queue); 
    } 
+0

Я действительно просто хочу первое исключение - это быстрый подход. Нет восстановления. Поэтому мне все равно, если я пропущу исключения 2..n. Я просто хочу первый. – Cheeso

2

Читает и пишет ссылки атомные (см C# Spec), и я почти уверен, что замок действительно создает барьер памяти да так, что вы делаете, это, вероятно, безопасно.

Но на самом деле просто используйте замок вокруг вашего чтения. Он гарантированно работает; если вы все видите, что к нему обращаются не в блокировке, вы знаете, что что-то не так, если блокировка вызывает проблемы с производительностью, вы слишком часто проверяете флаг, и это просто «правильная вещь».

+0

Проблема в том, что основной поток - это чтение этого поля _pendingException. Я бы предпочел НЕ брать блокировку на поток читателя, потому что он находится на горячей линии, и я бы очень часто снимал и отпускал блокировку. – Cheeso

+0

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

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