Многопоточность: когда я буду использовать Join? - PullRequest
60 голосов
/ 20 декабря 2010

В сети я вижу, что я использую myThread.Join();, когда хочу заблокировать свой поток, пока другой поток не завершится.(Одна вещь, которую я не понимаю, это то, что если у меня есть несколько потоков).

Но обычно я просто не понимаю, когда использую .Join() или условие, которое полезноза.Может кто-нибудь объяснить это мне, как будто я четвероклассник?Очень простое объяснение, чтобы понять, получит мой ответ.

Ответы [ 7 ]

66 голосов
/ 20 декабря 2010

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

List<Thread> workerThreads = new List<Thread>();
List<int> results = new List<int>();

for (int i = 0; i < 5; i++) {
    Thread thread = new Thread(() => {
        Thread.Sleep(new Random().Next(1000, 5000));
        lock (results) {
            results.Add(new Random().Next(1, 10));
        }
    });
    workerThreads.Add(thread);
    thread.Start();
}

// Wait for all the threads to finish so that the results list is populated.
// If a thread is already finished when Join is called, Join will return immediately.
foreach (Thread thread in workerThreads) {
    thread.Join();
}

Debug.WriteLine("Sum of results: " + results.Sum());

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

19 голосов
/ 20 декабря 2010

В следующем фрагменте кода главный поток вызывает Join () , что заставляет его ждать завершения всех порожденных потоков:

static void Main()
{
    Thread regularThread = new Thread(ThreadMethod);
    regularThread.Start();

    Thread regularThread2 = new Thread(ThreadMethod2);
    regularThread2.Start();

    // Wait for spawned threads to end.
    regularThread.Join();
    Console.WriteLine("regularThread returned.");

    regularThread2.Join();
    Console.WriteLine("regularThread2 returned.");
}

Обратите внимание, что если вы также раскручиваетепоток из пула потоков (например, с помощью QueueUserWorkItem), Join не будет ожидать этот фоновый поток.Вам нужно было бы реализовать какой-то другой механизм, такой как использование AutoResetEvent.

Для отличного введения в многопоточность я рекомендую прочитать бесплатную Threading в C # * 1010 Джо Албахари

12 голосов
/ 23 октября 2014

Это очень простая программа для демонстрации использования Thread Join. Пожалуйста, следуйте моим комментариям для лучшего понимания. Напишите эту программу как есть.

    using System;
    using System.Threading;


    namespace ThreadSample
    {
        class Program
        {
            static Thread thread1, thread2;
            static int sum=0;
            static void Main(string[] args)
            {
                start();
                Console.ReadKey();
            }
            private static void Sample() { sum = sum + 1; }
            private static void Sample2() { sum = sum + 10; }

            private static void start() 
            {    
                thread1 = new Thread(new ThreadStart(Sample));
                thread2 = new Thread(new ThreadStart(Sample2));
                thread1.Start();
                thread2.Start();
             // thread1.Join(); 
             // thread2.Join();
                Console.WriteLine(sum);
                Console.WriteLine();
            }
       }
}

1.Первый запуск как есть (с комментариями) : Тогда результат будет 0 (начальное значение) или 1 (когда закончен поток 1) или 10 (или закончен поток)

2. Выполните с удаление комментария thread1.Join(): Результат всегда должен быть больше чем 1 . Потому что thread1.Join() запущен, и поток 1 должен быть закончен до получения суммы. 1017 *

3. Выполнить с , удалив все комментарии : Результат должен быть всегда 11

10 голосов
/ 20 декабря 2010

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

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

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

3 голосов
/ 12 марта 2018

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

1 голос
/ 13 сентября 2017

Другой пример, когда ваш рабочий поток, скажем, читает из потока ввода, в то время как метод read может работать вечно, и вы хотите как-то этого избежать - применяя тайм-аут с использованием другого сторожевого потока:

// worker thread
var worker = new Thread(() => {
    Trace.WriteLine("Reading from stream");

    // here is the critical area of thread, where the real stuff happens
    // Sleep is just an example, simulating any real operation
    Thread.Sleep(10000);

    Trace.WriteLine("Reading finished");
}) { Name = "Worker" };
Trace.WriteLine("Starting worker thread...");
worker.Start();

// watchdog thread
ThreadPool.QueueUserWorkItem((o) => {
    var timeOut = 5000;
    if (!worker.Join(timeOut))
    {
        Trace.WriteLine("Killing worker thread after " + timeOut + " milliseconds!");
        worker.Abort();
    }
});
0 голосов
/ 23 октября 2015

Добавление задержки 300 мс в методе «Sample» и 400 мс в «Sample2» из поста devopsEMK облегчит понимание.

Поступая так, вы можете наблюдать это, удаляя комментарий из "thread1.Join ();"строка, главный поток ожидает завершения "thread1" и только после перемещения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...