Как получить список запущенных потоков в C#?

Я создаю динамические потоки в C# и мне нужно, чтобы получить статус этих запущенных потоков.

List<string>[] list;
list = dbConnect.Select();

for (int i = 0; i < list[0].Count; i++)
{
    Thread th = new Thread(() =>{
        sendMessage(list[0]['1']);
        //calling callback function
    });
    th.Name = "SID"+i;
    th.Start();
}

for (int i = 0; i < list[0].Count; i++)
{
    // here how can i get list of running thread here.
}

как можно получить список запущенных потоков?

5 ответов


создать List<Thread> и сохраните каждый новый поток в первом цикле for в нем.

List<string>[] list;
List<Thread> threads = new List<Thread>();
list = dbConnect.Select();

for (int i = 0; i < list[0].Count; i++)
{
    Thread th = new Thread(() =>{
        sendMessage(list[0]['1']);
        //calling callback function
    });
    th.Name = "SID"+i;
    th.Start();
    threads.add(th)
}

for (int i = 0; i < list[0].Count; i++)
{
    threads[i].DoStuff()
}

однако, если вам не нужно i вы можете сделать второй цикл foreach вместо for


в качестве примечания, если ваш sendMessage функция не занимает много времени, чтобы выполнить вы должны что-то более легкий вес, а затем полный поток, используйте класса ThreadPool.Метод queueuserworkitem или если он доступен для вас,задание


На Ниточках

Я бы избегал явно создавать потоки самостоятельно.

гораздо предпочтительнее использовать ThreadPool.QueueUserWorkItem или если вы можете использовать .Net 4.0, вы получите гораздо более мощный параллельных задач библиотека что также позволяет использовать потоки ThreadPool гораздо более мощным способом (Task.Factory.StartNew стоит посмотреть)

что делать, если мы решим пойти по подходу явного создания нити?

предположим, что ваш список[0].Count возвращает 1000 элементов. Предположим также, что вы выполняете это на высокопроизводительной (на момент написания этой статьи) машине 16core. Непосредственный эффект заключается в том, что у нас есть 1000 потоков конкурирующие для этих ограниченных ресурсов (16 ядер).

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

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

как преодолеть 'за'-потоков?

вот тут ThreadPool вступает в игру.

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

как они работают:

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

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

таким образом, мы можем видеть, что с помощью потоков пула потоков мы более эффективны как

  • С точки зрения максимальной фактической работы getting done. Поскольку мы не насыщаем процессоры потоками, меньше времени тратится на переключение между потоками и больше времени на выполнение кода, который должен выполнять поток.
  • более быстрый запуск потока: каждый поток threadpool легко доступен, в отличие от ожидания создания нового потока.
  • С точки зрения минимизация потребления памяти, threadpool ограничит количество потоков размером threadpool, запрашивая любые запросы, которые выходят за пределы ограничения размера threadpool. (см. ThreadPool.GetMaxThreads). Основной причиной этого выбора, конечно, так что мы не перенасыщают ограниченное количество ядер слишком много запросов, сохраняя нить связи перехода на более низкие уровни.

слишком много теории, давайте проверим всю эту теорию!

хорошо, приятно знать все это в теории, но давайте на практике и посмотрим, что цифры говорят нам, с упрощенной грубой версией приложения, которая может дать нам грубое указание на разницу в порядках величины. Мы сделаем сравнение между новым потоком, ThreadPool и параллельной библиотекой задач (TPL)

новая тема

    static void Main(string[] args)
    {
        int itemCount = 1000;

        Stopwatch stopwatch = new Stopwatch(); 
        long initialMemoryFootPrint = GC.GetTotalMemory(true);

        stopwatch.Start();
        for (int i = 0; i < itemCount; i++)
        {
            int iCopy = i;  // You should not use 'i' directly in the thread start as it creates a closure over a changing value which is not thread safe. You should create a copy that will be used for that specific variable.
            Thread thread = new Thread(() =>
            {
                // lets simulate something that takes a while
                int k = 0;
                while (true)
                {
                    if (k++ > 100000)
                        break;
                }

                if ((iCopy + 1) % 200 == 0) // By the way, what does your sendMessage(list[0]['1']); mean? what is this '1'? if it is i you are not thread safe.
                    Console.WriteLine(iCopy + " - Time elapsed: (ms)" + stopwatch.ElapsedMilliseconds);
            });

            thread.Name = "SID" + iCopy; // you can also use i here. 
            thread.Start();
        }

        Console.ReadKey();
        Console.WriteLine(GC.GetTotalMemory(false) - initialMemoryFootPrint);
        Console.ReadKey();
    }

результат:

New Thread Benchmark

класса ThreadPool.EnqueueUserWorkItem

    static void Main(string[] args)
    {
        int itemCount = 1000;

        Stopwatch stopwatch = new Stopwatch(); 
        long initialMemoryFootPrint = GC.GetTotalMemory(true);

        stopwatch.Start();

        for (int i = 0; i < itemCount; i++)
        {
            int iCopy = i; // You should not use 'i' directly in the thread start as it creates a closure over a changing value which is not thread safe. You should create a copy that will be used for that specific variable.
            ThreadPool.QueueUserWorkItem((w) =>
            {
                // lets simulate something that takes a while
                int k = 0;
                while (true)
                {
                    if (k++ > 100000)
                        break;
                }

                if ((iCopy + 1) % 200 == 0) 
                    Console.WriteLine(iCopy + " - Time elapsed: (ms)" + stopwatch.ElapsedMilliseconds);
            });
        }

        Console.ReadKey();
        Console.WriteLine("Memory usage: " + (GC.GetTotalMemory(false) - initialMemoryFootPrint));
        Console.ReadKey();
    }

результат:

ThreadPool Benchmark

параллельная библиотека задач (TPL)

    static void Main(string[] args)
    {
        int itemCount = 1000;

        Stopwatch stopwatch = new Stopwatch(); 
        long initialMemoryFootPrint = GC.GetTotalMemory(true);

        stopwatch.Start();
        for (int i = 0; i < itemCount; i++)
        {
            int iCopy = i;  // You should not use 'i' directly in the thread start as it creates a closure over a changing value which is not thread safe. You should create a copy that will be used for that specific variable.
            Task.Factory.StartNew(() =>
            {
                // lets simulate something that takes a while
                int k = 0;
                while (true)
                {
                    if (k++ > 100000)
                        break;
                }

                if ((iCopy + 1) % 200 == 0) // By the way, what does your sendMessage(list[0]['1']); mean? what is this '1'? if it is i you are not thread safe.
                    Console.WriteLine(iCopy + " - Time elapsed: (ms)" + stopwatch.ElapsedMilliseconds);
            });
        }

        Console.ReadKey();
        Console.WriteLine("Memory usage: " + (GC.GetTotalMemory(false) - initialMemoryFootPrint));
        Console.ReadKey();
    }

результат:

Task Parallel Library result

Итак, мы видим, что:

+--------+------------+------------+--------+
|        | new Thread | ThreadPool |  TPL   |
+--------+------------+------------+--------+
| Time   | 6749       | 228ms      | 222ms  |
| Memory | ≈300kb     | ≈103kb     | ≈123kb |
+--------+------------+------------+--------+

выше падает красиво в соответствии с тем, что мы ожидали в теории. Высокая память для нового потока, а также более низкая общая производительность по сравнению с ThreadPool. ThreadPool и TPL имеют эквивалентную производительность с TPL, имеющим немного более высокий объем памяти, чем чистый пул потоков, но, вероятно, это цена, которую стоит заплатить, учитывая дополнительные задачи гибкости (такие как отмена, ожидание завершения запроса статуса задачи)

на данный момент мы доказали, что использование потоков ThreadPool является предпочтительный вариант с точки зрения скорости и памяти.

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

чтобы ответить на ваш вопрос

учитывая идеи, которые мы собрали, вот как я бы подошел к этому:

        List<string>[] list = listdbConnect.Select()
        int itemCount = list[0].Count;
        Task[] tasks = new Task[itemCount];
        stopwatch.Start();
        for (int i = 0; i < itemCount; i++)
        {
            tasks[i] = Task.Factory.StartNew(() =>
            {
                // NOTE: Do not use i in here as it is not thread safe to do so! 
                sendMessage(list[0]['1']);
                //calling callback function
            });
        }

        // if required you can wait for all tasks to complete
        Task.WaitAll(tasks);

        // or for any task you can check its state with properties such as: 
        tasks[1].IsCanceled
        tasks[1].IsCompleted
        tasks[1].IsFaulted 
        tasks[1].Status

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

удачи!


использовать Process.Threads:

var currentProcess = Process.GetCurrentProcess();
var threads = currentProcess.Threads;

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

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


Process.GetCurrentProcess().Threads

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


использовать Process.Threads для итерации по вашим потокам.