Как выполнить 2 последовательные команды в потоке без переключения контекста?

#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 ?