2013-08-25 4 views
4

Я начинаю несколько потоков и хочу узнать, когда любой после этого заканчивается. Я знаю, что следующий код:Подождите, пока НИКАКАЯ нить не закончится, а не ВСЕ

foreach (Thread t in threads) 
    t.Join(); 

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

Некоторые уточнения: Я работаю над инструментом ведения журнала/отслеживания, который должен регистрировать деятельность приложения. Я могу вставлять логические операторы при запуске потока, но я не могу вставить оператор журнала на все возможные пути из потока (несколько точек выхода, исключения и т. Д.). Поэтому я хотел бы зарегистрировать новый поток и затем получить уведомление, когда он закончит писать запись в журнале. Я мог асинхронно Join на каждом потоке, но это означает второй поток для каждого контролируемого потока, который может показаться немного большим. Нитки используются различными способами, будь то BackgroundWorker, Task или пул. По сути, это нить, и я хотел бы знать, когда это будет сделано. Точный механизм потока определяется приложением, а не протоколирующим решением.

+0

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

+0

Я добавил больше информации о том, что я пытаюсь сделать. Конечно, мне нужно знать, какой поток закончен, иначе это было бы бесполезно в этом случае. – ygoe

+0

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

ответ

1

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

Возьмите преимущество Thread.Join(int) метода, который принимает и возвращает millisecond timeouttrue когда нить обрывается или false, когда истек.

List<Thread> threads = new List<Thread>(); 

while (!threads.Any(x=> x.Join(100))) 
{ 

} 

Вы можете изменить тайм-аут Join Если вы знаете, как долго он будет продолжаться.

+0

Я думал, что это будет предложено (забыл упомянуть об этом). Меня просто беспокоит постоянная активность в потоке мониторинга, которая может увеличить использование энергии. Чтобы получить приличное временное разрешение, я думаю, мне понадобится не более 100 мс. – ygoe

+0

Итак, это отвечает на ваш вопрос или вы ищете что-то еще. –

+0

Вы должны проверить IsAlive ... Join просто вызовет Join на каждом из них. Кроме того, вы должны послать поток спать на каждой итерации, иначе вы просто будете в спине. –

0

Вы можете использовать рабочий стол background для ваших рабочих потоков.

Затем подключите все RunWorkerCompleted событий к способу, который будет ждать их.

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

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

+0

См. Мои разъяснения. – ygoe

5

Вместо резьбы использовать Tasks. Он имеет метод WaitAny.

Task.WaitAny 

Как вы можете прочитать here,

  • Более эффективное и более масштабируемое использование системных ресурсов.
  • Более программный контроль, чем возможен с помощью нити или рабочего элемента.
+1

См. Мои разъяснения. У меня только «Thread.Current», но он был запущен. – ygoe

1

Ответы основаны на разъяснении, что все, что у вас есть, это Thread.Current. Отказ от ответственности: ИМО, что вы пытаетесь сделать, это взломать, таким образом, моя идея - это, безусловно, взлом.

Итак, используйте отражение, чтобы получить набор собственных дескрипторов Win32 для ваших нужных потоков. Ваш поиск Thread.GetNativeHandle метод internal, поэтому вы называете это thread.GetType().InvokeMember("GetNativeHandle", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic, ...). Используйте инструмент отражения по вашему выбору или Framework sources, чтобы узнать больше об этом. После того, как у вас есть ручки, идти с одним из следующих вариантов:

  • Настройка собственной реализации SynchronizationContext (производные от него) и использовать SynchronizationContext.WaitHelper(waitAll: false) ждать ваших неуправляемые ручек.

  • Использовать необработанный API Win32, например WaitForMultipleObjects или CoWaitForMultipleObjects (в зависимости от того, нужно ли передавать сообщения).

Выполнение ожидания отдельного дочернего элемента или потока бассейна.

[Изменено] В зависимости от исполнения среды ваших целевых потоков, этот хак не может работать, потому что один-к-одному между управляемыми и неуправляемыми потоками is not guaranteed:

можно определить Windows, который выполняет код управляемого потока и извлекает его дескриптор. Тем не менее, по-прежнему не имеет смысла вызывать функцию SetThreadAffinityMask для этого потока Windows, , потому что управляемый планировщик может продолжить выполнение управляемого потока в другом потоке Windows.

Возможно, это может быть implication только для пользовательских хостов CLR. Также представляется возможным, чтобы control managed thread affinity с Thread.BeginThreadAffinity и Thread.EndThreadAffinity.

0

Не могли бы вы рассмотреть возможность переноса ваших вызовов потока с помощью другой ветки? Таким образом, вы можете регистрировать синхронно до & после запуска потока.

Что-то вроде этого псевдокода:

int threadLogger(<parms>) { 
    log("starting thread"); 
    retcode = ActualThreadBody(<parms>); 
    log("exiting thread"); 
    return retcode; 
} 

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

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