#c# #multithreading #task #context-switch
#c# #многопоточность #задача #переключение контекста
Вопрос:
У меня есть программа на C #, в которой есть класс Agent. Программа создает несколько агентов, и у каждого агента есть метод «run()«, который выполняет задачу (т.е.: Задача.Factory.StartNew()…).
Каждый агент выполняет некоторые вычисления, а затем должен дождаться, пока все остальные агенты завершат свои вычисления, прежде чем перейти к следующему этапу (его действия будут основываться на вычислениях других).
Чтобы заставить агента ждать, я создал CancellationTokenSource (с именем «tokenSource»), и чтобы предупредить программу о том, что этот агент переходит в спящий режим, я запустил событие. Таким образом, 2 последовательные команды являются:
(1) OnWaitingForAgents(new EventArgs());
(2) tokenSource.Token.WaitHandle.WaitOne();
(Событие перехватывается классом «AgentManager», который сам по себе является потоком, и 2-я команда переводит поток задач агента в спящий режим до тех пор, пока не будет получен сигнал для токена отмены).
Каждый раз, когда запускается вышеупомянутое событие, класс AgentManager перехватывает его и добавляет 1 к счетчику. Если номер счетчика равен количеству агентов, используемых в программе, AgentManager (который содержит ссылки на все агенты) запускает каждый из них следующим образом:
agent.TokenSource.Cancel();
Теперь мы подходим к моей проблеме: 1-я команда выполняется агентом асинхронно, затем из-за переключения контекста между потоками AgentManager, похоже, улавливает событие и продолжает пробуждать всех агентов. НО — текущий агент еще даже не добрался до 2-й команды!
Таким образом, Агент получает сигнал «пробуждение», и только после этого он переходит в спящий режим, что означает, что он застревает во сне, и никто не может его разбудить!
Есть ли способ «атомизировать» 2 последовательных метода вместе, чтобы не происходило переключения контекста, тем самым вынуждая агента перейти в спящий режим до того, как у AgentManager появится шанс разбудить его?
Ответ №1:
Низкоуровневый метод, о котором вы спрашиваете, — это синхронизация потоков. У вас есть критический раздел (или часть одного), и вам нужно защитить доступ к нему. Я удивлен, что вы узнали о многопоточном программировании, еще не изучив синхронизацию потоков и критические разделы! Об этих вещах важно знать при любом «низкоуровневом» многопоточном программировании.
Ответ №2:
Может быть, заглянуть в Parallel.Вызывать или параллельно.Для в .NET 4, который позволяет выполнять методы параллельно и ждать, пока не будут вызваны все параллельные методы.
http://msdn.microsoft.com/en-us/library/dd992634.aspx
Похоже, это вам очень помогло бы и позаботилось обо всех очередях за вас.
Ответ №3:
хм… Я не думаю, что это хорошая идея (или даже возможно) разрабатывать программное обеспечение на .NET, беспокоясь о переключениях контекста, поскольку ни Windows, ни .NET не работают в режиме реального времени. Вероятно, у вас проблема другого рода в этом коде.
Я понял, что вы просто запускаете все свои агенты параллельно и хотите дождаться, пока все они закончат, чтобы перейти к следующему этапу. Вы можете использовать несколько методов для достижения этой цели, самым простым из которых было бы использование Monitor.Wait(Object monitor)
и Monitor.PulseAll(Object monitor)
.
В библиотеке задач также есть несколько способов сделать это. Как указал @jishi, вы можете использовать Parallel
варианты или создать много Task
вариантов, а затем дождаться всех с помощью Task.WaitAll(Task[] tasks)
метода.
Каждый раз, когда запускается вышеупомянутое событие, класс AgentManager перехватывает его и добавляет 1 к счетчику.
Как вы добавляете 1 к этому счетчику и как вы его считываете? Вы должны использовать Interloked.Increment
для обеспечения атомарной операции и прочитать ее в изменяемой операции с Thread.VolatileRead
, например, или просто поместить ее в оператор lock.
Комментарии:
1. Важно беспокоиться о переключениях контекста между потоками при выполнении низкоуровневого многопоточного программирования.
2. С «переключением контекста» мы говорим о том факте, что процессор переключается на другой контекст потока каждые x миллисекунд, чтобы обеспечить ощущение параллельного выполнения? как вы управляете этим из .NET ?