Запуск нескольких потоков и отслеживание их из моего приложения.NET

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

Пример ==> Запускает Альфу Потока, Запускает Бету Потока.. тогда в любой точке в моем приложении я должен быть в состоянии сказать Оконечную Бету Потока..

Что лучший способ состоит в том, чтобы отслеживать открытые потоки в.NET и что я должен знать (идентификатор?) о потоке для завершения его? Пример кода, учебное руководство было бы полезно.

17
задан David Basarab 1 March 2010 в 13:42
поделиться

7 ответов

Вы можете избавить себя от лишней работы и использовать этот Smart Thread Pool . Он предоставляет систему единиц работы, которая позволяет вам запрашивать статус каждого потока в любой момент и завершать их.

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

public class MyThreadPool
{
    private IList<Thread> _threads;
    private readonly int MAX_THREADS = 25;

    public MyThreadPool()
    {
        _threads = new List<Thread>();
    }

    public void LaunchThreads()
    {
        for (int i = 0; i < MAX_THREADS;i++)
        {
            Thread thread = new Thread(ThreadEntry);
            thread.IsBackground = true;
            thread.Name = string.Format("MyThread{0}",i);

            _threads.Add(thread);
            thread.Start();
        }
    }

    public void KillThread(int index)
    {
        string id = string.Format("MyThread{0}",index);
        foreach (Thread thread in _threads)
        {
            if (thread.Name == id)
                thread.Abort();
        }
    }

    void ThreadEntry()
    {

    }
}

Вы, конечно, можете значительно усложнить задачу. Если уничтожение ваших потоков не зависит от времени (например, если вам не нужно убивать поток за 3 секунды в пользовательском интерфейсе), тогда Thread.Join () - лучшая практика.

И если вы еще не читали его, то у Джона Скита есть это хорошее обсуждение и решение для совета «не использовать прерывание», которое является общим для SO.

15
ответ дан 30 November 2019 в 13:05
поделиться

Я задал похожие вопросы и получил несколько хороших ответов: Завершение работы многопоточного приложения

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

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

Thread thread = new Thread(new ThreadStart(testObject.RunLoop));
thread.IsBackground = true;
thread.start();

Предпочтительный способ запуска и управления потоками - в ThreadPool , но практически любой контейнер можно использовать для хранения ссылки на ваши потоки. У ваших потоков всегда должен быть флаг, который сообщит им о завершении, и они должны постоянно его проверять.

Кроме того, для лучшего контроля вы можете снабдить свои потоки CountdownLatch : ​​всякий раз, когда поток выходит из цикла, он будет сигнализировать о CountdownLatch . Ваш основной поток вызовет метод CountdownLatch.Wait () , и он будет блокироваться до тех пор, пока все потоки не сообщат сигнал ... это позволяет вам правильно выполнить очистку и гарантирует, что все ваши потоки завершатся, прежде чем вы начнете очистку .

public class CountdownLatch
{
    private int m_remain;
    private EventWaitHandle m_event;

    public CountdownLatch(int count)
    {
        Reset(count);
    }

    public void Reset(int count)
    {
        if (count < 0)
            throw new ArgumentOutOfRangeException();
        m_remain = count;
        m_event = new ManualResetEvent(false);
        if (m_remain == 0)
        {
            m_event.Set();
        }
    }

    public void Signal()
    {
        // The last thread to signal also sets the event.
        if (Interlocked.Decrement(ref m_remain) == 0)
            m_event.Set();
    }

    public void Wait()
    {
        m_event.WaitOne();
    }
}

Также стоит упомянуть, что метод Thread.Abort () делает некоторые странные вещи:

Когда поток вызывает на себе Abort, эффект аналогичен генерированию исключения . ; исключение ThreadAbortException происходит немедленно, и результат предсказуем.Однако, если один поток вызывает Abort в другом потоке, прерывание прерывает выполнение любого кода . Также существует вероятность того, что статический конструктор может быть прерван. В редких случаях это может помешать созданию экземпляров этого класса в этом приложении домен. В .NET Framework версий 1.0 и 1.1 существует вероятность прерывания потока во время выполнения блока finally , и в этом случае finally блок прерван.

Поток, который вызывает Abort, может заблокироваться, если поток, который прерывается, находится в защищенной области кода , такой как блок catch, наконец {{1 }} блок или область ограниченного выполнения . Если поток, вызывающий Abort , удерживает блокировку, которую требует прерванный поток , может возникнуть взаимоблокировка.

3
ответ дан 30 November 2019 в 13:05
поделиться

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

var myThread = (select thread from threads where thread.Name equals "myThread").FirstOrDefault();
if(myThread != null)
    myThread.Abort();
2
ответ дан 30 November 2019 в 13:05
поделиться

Зависит от того, насколько сложным он вам нужен. Вы можете реализовать свой собственный тип ThreadPool с помощью вспомогательных методов и т. Д. Однако я думаю, что это так же просто, как просто поддерживать список / массив и соответственно добавлять / удалять потоки в / из коллекции.

Вы также можете использовать коллекцию Dictionary и использовать свой собственный тип определенного ключа для их извлечения, то есть Guids / strings.

1
ответ дан 30 November 2019 в 13:05
поделиться

При запуске каждого потока помещайте его ManagedThreadId в словарь в качестве ключа и экземпляр потока в качестве значения. Используйте обратный вызов из каждого потока, чтобы вернуть его ManagedThreadId, который можно использовать для удаления потока из словаря при его завершении. Вы также можете пройти по словарю, чтобы при необходимости прервать потоки. Сделайте фоновые потоки потоками так, чтобы они завершались, если ваше приложение неожиданно завершает работу.

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

1
ответ дан 30 November 2019 в 13:05
поделиться

Вау, ответов так много ..

  1. Вы можете просто использовать массив для хранения потоков, это будет работать только в том случае, если доступ к массиву будет последовательным, но если у вас будет другой поток, обращающийся к этому массив, вам потребуется синхронизировать доступ
  2. Вы можете использовать пул потоков, но пул потоков очень ограничен и может содержать только фиксированное количество потоков.
  3. Как упоминалось выше, вы можете создать собственный пул потоков, что в .NET v4 стало намного проще с введением безопасных коллекций.
  4. вы можете управлять ими, удерживая список объектов мьютекса, который определит, когда эти потоки должны завершиться, потоки будут запрашивать мьютекс каждый раз, когда они запускаются, прежде чем делать что-либо еще, и если он установлен, завершение, вы можете управлять мьютами. откуда угодно, и поскольку мьютексы по определению являются потокобезопасными, это довольно просто ..

я могу придумать еще 10 способов, но они, похоже, работают. дайте мне знать, если они вам не подходят.

1
ответ дан 30 November 2019 в 13:05
поделиться

Вы можете создать Словарь потоков и назначить им идентификаторы, например:

Dictionary<string, Thread> threads = new Dictionary<string, Thread>();
for(int i = 0 ;i < numOfThreads;i++)
{
    Thread thread = new Thread(new ThreadStart(MethodToExe));
    thread.Name = threadName; //Any name you want to assign
    thread.Start(); //If you wish to start them straight away and call MethodToExe
    threads.Add(id, thread);
}

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

И когда вы хотите прекратить их, вы можете прервать их. Лучше иметь в вашем MethodToExe какое-то условие, которое позволяет этому методу уйти, позволяя потоку завершиться корректно. Примерно так:

void MethodToExe()
{
   while(_isRunning)
   {
      //you code here//
      if(!_isRunning)
      {
          break;
      }
      //you code here//
   }
}

Чтобы прервать выполнение, вы можете перечислить словарь и вызвать Thread.Abort () . Будьте готовы поймать ThreadAbortException

3
ответ дан 30 November 2019 в 13:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: