Я пытаюсь написать код многопоточности и сталкиваюсь с некоторыми вопросами о синхронизации. Я знаю, что существует много сообщений здесь, но я не мог найти ничего, что соответствует.
У меня есть a System.Timers.Timer
это протекало каждые 30 секунд, это переходит к дб и проверяет, существуют ли какие-либо новые задания. Если он находит один, он выполняет задание на текущем потоке (таймер открытый новый поток для каждого прошедшего). В то время как задание работает, я должен уведомить основной поток (где таймер) о прогрессе.
Примечания:
beginInvoke
(или используйте фоновый поток) как я обычно делаю в winforms.ISynchronizeInvoke
на моем основном классе, но это смотрит немного излишества (возможно, я неправ здесь).Мой вопрос:
Что правильный путь состоит в том, чтобы уведомить мой основной поток о каком-либо прогрессе моего потока задания?
Спасибо за любую справку.
Вы также можете использовать блокировку
для реализации поточно-ориентированного класса JobManager
, который отслеживает ход выполнения различных рабочих потоки. В этом примере я просто поддерживаю количество активных рабочих потоков, но его можно расширить для ваших отчетов о ходе выполнения.
class JobManager
{
private object synchObject = new object();
private int _ActiveJobCount;
public int ActiveJobsCount
{
get { lock (this.synchObject) { return _ActiveJobCount; } }
set { lock (this.synchObject) { _ActiveJobCount = value; } }
}
public void Start(Action job)
{
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (sender, e) =>
{
this.ActiveJobsCount++;
job();
this.ActiveJobsCount--;
};
timer.Start();
}
}
Пример:
class Program
{
public static void Main(string[] args)
{
var manager = new JobManager();
manager.Start(() => Thread.Sleep(3500));
while (true)
{
Console.WriteLine(manager.ActiveJobsCount);
Thread.Sleep(250);
}
}
}
Вы может уведомить основной поток о ходе выполнения с помощью метода обратного вызова. То есть:
// in the main thread
public void ProgressCallback(int jobNumber, int status)
{
// handle notification
}
Вы можете передать этот метод обратного вызова рабочему потоку, когда вы его вызываете (то есть как делегат), или код рабочего потока может «знать» об этом неявно. В любом случае работает.
Параметры jobNumber и status являются лишь примерами. Возможно, вы захотите использовать другой способ идентификации выполняемых заданий, и вы можете захотеть использовать перечислимый тип для статуса. Однако имейте в виду, что ProgressCallback будет вызываться несколькими потоками одновременно, поэтому, если вы обновляете какие-либо общие структуры данных или записываете информацию журнала, вам придется защищать эти ресурсы с помощью блокировок или других методов синхронизации.
Вы также можете использовать для этого события, но поддержание актуальности подписок на события основного потока может быть потенциальной проблемой. У вас также есть вероятность утечки памяти, если вы забудете отписать основной поток от событий определенного рабочего потока. Хотя события, безусловно, будут работать, я бы порекомендовал обратный вызов для этого приложения.
Если вы не возражаете против .NET 3.0, вы можете использовать диспетчер Dispatcher для распределения запросов между потоками. Он ведет себя аналогично Control.Invoke () в Windows Forms, но не имеет зависимости от Forms. Однако вам нужно будет добавить ссылку на сборку WindowsBase (часть .NET 3.0 и новее и является основой для WPF)
Если вы не можете зависеть от .NET 3.0, то я бы скажем, вы с самого начала выбрали правильное решение: реализуйте интерфейс ISynchronizeInvoke в своем основном классе и передайте его свойству SynchronizingObject таймера. Затем обратный вызов вашего таймера будет вызван в основном потоке, который затем может порождать BackgroundWorkers , который проверяет БД и запускает любые задания в очереди.Задания будут сообщать о ходе выполнения с помощью события ProgressChanged , которое автоматически выполняет маршалинг вызова в основной поток.
Быстрый поиск в Google показал этот пример того, как на самом деле реализовать интерфейс ISynchronizeInvoke.
Использовать события. Например, класс BackgroundWorker разработан специально для того, что вы имеете в виду.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Функция ReportProgress
вместе с событием ProgressChanged
что бы вы использовали для обновления прогресса.
pullJobTimer.Elapsed += (sender,e) =>
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += (s,e) =>
{
// Whatever tasks you want to do
// worker.ReportProgress(percentComplete);
};
worker.ProgressChanged += mainThread.ProgressChangedEventHandler;
worker.RunWorkerAsync();
};