Как создать несколько потоков и подождать их завершения?Создайте несколько потоков и дождитесь их завершения
ответ
Это зависит от того, какую версию .NET Framework вы используете. .NET 4.0 делает управление потоками намного проще с помощью задач:
class Program
{
static void Main(string[] args)
{
Task task1 = Task.Factory.StartNew(() => doStuff());
Task task2 = Task.Factory.StartNew(() => doStuff());
Task task3 = Task.Factory.StartNew(() => doStuff());
Task.WaitAll(task1, task2, task3);
Console.WriteLine("All threads complete");
}
static void doStuff()
{
//do stuff here
}
}
В предыдущих версиях .NET вы можете использовать BackgroundWorker
объект, используйте ThreadPool.QueueUserWorkItem()
, или создавать свои темы вручную и использовать Thread.Join()
ждать их, чтобы завершить :
static void Main(string[] args)
{
Thread t1 = new Thread(doStuff);
t1.Start();
Thread t2 = new Thread(doStuff);
t2.Start();
Thread t3 = new Thread(doStuff);
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine("All threads complete");
}
В .Net 4.0 вы можете использовать Task Parallel Library.
В более ранних версиях вы можете создать список объектов Thread
в цикле, вызывая Start
на каждом из них, затем выполните еще один цикл и вызовите Join
на каждом из них.
зачем нужен еще один цикл? – user496949
@user: Если вы вызываете 'Join' сразу после запуска потока, вы в конечном итоге ожидаете его завершения до начала любых других потоков. Вам нужно запустить _все_ потоков, ** затем ** «Присоединить» все из них. – SLaks
вы абсолютно правы @SLaks :) –
Думаю, что вам нужно WaitHandler.WaitAll. Вот пример:
public static void Main(string[] args)
{
int numOfThreads = 10;
WaitHandle[] waitHandles = new WaitHandle[numOfThreads];
for (int i = 0; i < numOfThreads; i++)
{
var j = i;
// Or you can use AutoResetEvent/ManualResetEvent
var handle = new EventWaitHandle(false, EventResetMode.ManualReset);
var thread = new Thread(() =>
{
Thread.Sleep(j * 1000);
Console.WriteLine("Thread{0} exits", j);
handle.Set();
});
waitHandles[j] = handle;
thread.Start();
}
WaitHandle.WaitAll(waitHandles);
Console.WriteLine("Main thread exits");
Console.Read();
}
EDIT FCL имеет несколько более удобных функций.
(1) Task.WaitAll, а также его перегрузки, когда вы хотите выполнять некоторые задачи параллельно (и без возвращаемых значений).
var tasks = new[]
{
Task.Factory.StartNew(() => DoSomething1()),
Task.Factory.StartNew(() => DoSomething2()),
Task.Factory.StartNew(() => DoSomething3())
};
Task.WaitAll(tasks);
(2) Task.WhenAll, когда вы хотите сделать некоторые задачи с возвращаемые значения, он выполняет операции, и помещает результаты в массив. Это поточно-безопасный, вам не нужно использовать поточно-безопасный контейнер и реализовать операцию добавления самостоятельно.
var tasks = new[]
{
Task.Factory.StartNew(() => GetSomething1()),
Task.Factory.StartNew(() => GetSomething2()),
Task.Factory.StartNew(() => GetSomething3())
};
var things = Task.WhenAll(tasks);
@Kirk: Я собирался добавить пример только сейчас, но должен был пойти на собрание. –
приятно! +1 (и удаленный комментарий) –
хорошее объяснение для значений «нет возврата» и «возврата» из метода! –
Не знаю, есть ли лучший способ, но после описывает, как я сделал это с противотоком и фона рабочего потока.
private object _lock=new object();
private int _runningThreads = 0;
private int Counter{
get{
lock(_lock)
return _runningThreads;
}
set{
lock(_lock)
_runningThreads = value;
}
}
Теперь, когда вы создаете рабочий поток, увеличить счетчик:
var t=new BackgroundWorker();
//ADD RunWorkerCompleted HANDLER
//START THREAD
Counter++;
В работе завершена, уменьшает счетчик:
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Counter--;
}
Теперь вы можете проверить для счетчика в любое время, чтобы см., если какая-либо нить работает:
if(Couonter>0){
//SOME THREAD IS YET TO FINISH.
}
Вам не нужен замок. На самом деле, поскольку вы только записываете форму свойства в поток пользовательского интерфейса, вам ничего не нужно. – SLaks
Возможно, я ошибаюсь, но я думаю, что замок правильный; он уменьшает счетчик в обработчике событий, который запускается, когда каждый рабочий завершает работу. –
@Mark: событие Completed всегда запускается в потоке пользовательского интерфейса. Кроме того, замок не будет иметь никакого эффекта; Чтение и запись Int32 являются атомарными. Если бы были проблемы с потоком, блокировка не помогла бы; ему нужно будет вызвать 'Interlocked.Increment'. – SLaks
Я сделал очень простой метод расширения, чтобы ждать все нити коллекции:
using System.Collections.Generic;
using System.Threading;
namespace Extensions
{
public static class ThreadExtension
{
public static void WaitAll(this IEnumerable<Thread> threads)
{
if(threads!=null)
{
foreach(Thread thread in threads)
{ thread.Join(); }
}
}
}
}
Тогда вы просто звоните:
List<Thread> threads=new List<Thread>();
//Add your threads to this collection
threads.WaitAll();
Я бы предпочел использовать 'ThreadHelpers.WaitAll (threadCollection)' .. в любом случае, это во многом то, что я использую для тестов. Мне редко приходилось «ждать все» в реальном коде. – user2864740
Большинство предложенных ответов не принимать во внимание, а время- который очень важен для предотвращения возможного взаимоблокировки. Далее мой образец кода. (Обратите внимание, что я в первую очередь разработчик Win32, и именно так я бы это сделал.)
//'arrRunningThreads' = List<Thread>
//Wait for all threads
const int knmsMaxWait = 3 * 1000; //3 sec timeout
int nmsBeginTicks = Environment.TickCount;
foreach(Thread thrd in arrRunningThreads)
{
//See time left
int nmsElapsed = Environment.TickCount - nmsBeginTicks;
int nmsRemain = knmsMaxWait - nmsElapsed;
if(nmsRemain < 0)
nmsRemain = 0;
//Then wait for thread to exit
if(!thrd.Join(nmsRemain))
{
//It didn't exit in time, terminate it
thrd.Abort();
//Issue a debugger warning
Debug.Assert(false, "Terminated thread");
}
}
Не отвечает на вопрос. В любом случае, почему бы вам просто не использовать Task.Wait (timeout)? –
Если вы не хотите использовать класс задач (например, в .NET 3.5), вы можете просто начать все ваши темы, а затем добавить их в список и присоединиться к ним в цикле Еогеасп.
Пример:
List<Thread> threads = new List<Thread>();
// Start threads
for(int i = 0; i<10; i++)
{
int tmp = i; // copy value for closure
Thread t = new Thread(() => Console.WriteLine(tmp));
t.Start;
threads.Add(t);
}
// Await threads
foreach(Thread thread in threads)
{
thread.Join();
}
В моем случае я не мог создать экземпляр свои объекты на пуле потоков с Task.Run()
или Task.Factory.StartNew()
. Они не синхронизировали моих давно работающих делегатов правильно. Мне нужно, чтобы делегаты выполняли асинхронно, приостанавливая мой основной поток для их коллективного завершения. Thread.Join()
не будет работать, так как я хотел дождаться коллективного завершения в середине родительского потока, а не в конце. С Task.Run()
или Task.Factory.StartNew()
либо все дочерние потоки заблокированы друг от друга, либо родительский поток не будет заблокирован ... Я не мог понять, как идти с делегатами async
из-за повторной сериализации синтаксиса await
.
Вот мое решение, используя темы вместо задачи:
using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.ManualReset))
{
int outdex = mediaServerMinConnections - 1;
for (int i = 0; i < mediaServerMinConnections; i++)
{
new Thread(() =>
{
sshPool.Enqueue(new SshHandler());
if (Interlocked.Decrement(ref outdex) < 1)
wh.Set();
}).Start();
}
wh.WaitOne();
}
(Извинения для плотного стиля кодирования.)
- 1. Дождитесь завершения дочерних потоков: Java Описание
- 2. Как запустить несколько потоков и ждать полного их завершения
- 3. Подсчитайте количество запущенных скриптов и дождитесь их завершения
- 4. Заверните кучу асинхронных операций и дождитесь их завершения?
- 5. Исполнители Java: дождитесь завершения задачи.
- 6. Дождитесь завершения асинхронного запроса
- 7. Транспортир Дождитесь завершения анимации
- 8. Отмените задачу и дождитесь ее завершения.
- 9. Дождитесь завершения функции
- 10. Дождитесь завершения COM-события
- 11. Дождитесь завершения условия string.contains()
- 12. Дождитесь сборки Jenkins завершения
- 13. Дождитесь завершения прослушивания Android
- 14. Дождитесь завершения метода Async
- 15. Node.js дождитесь завершения запроса
- 16. Дождитесь завершения NSURLConnection
- 17. Дождитесь завершения NSURLConnection sendAsynchronousRequest
- 18. Дождитесь завершения задачи async
- 19. Дождитесь завершения нескольких процессов
- 20. Дождитесь завершения Application.Calculate
- 21. Дождитесь завершения асинхронной функции
- 22. ImageView - измените изображение и дождитесь завершения макета
- 23. Загрузите несколько файлов async и дождитесь их завершения до выполнения остальной части кода
- 24. Chrome Extension создайте новую вкладку, дождитесь ее завершения, выполните сценарий
- 25. Дождитесь завершения целого набора AsyncTask
- 26. Дождитесь завершения всех прослушивателей событий
- 27. node.js: дождитесь окончания всех потоков
- 28. NodeJS - Bluebird: дождитесь завершения цикла
- 29. Дождитесь запуска всех потоков, прежде чем запускать их
- 30. asio - Дождитесь завершения операций async
API-интерфейс задач на сегодняшний день является самым простым решением. – JefClaes
прост и понятен..thanks :) –
Есть некоторые ограничения, о которых нужно знать - если вам требуется, чтобы поток имел определенный приоритет, то вы не можете использовать Задачи. Ну, технически вы могли бы, но это плохая идея, чтобы изменить приоритет потока потока внутри Task, потому что поток принадлежит файлу threadpool, и поэтому ваш пользовательский приоритет может повлиять на некоторый другой код. – JustAMartin