синхронизируйте потоки - никакой UI

Я пытаюсь написать код многопоточности и сталкиваюсь с некоторыми вопросами о синхронизации. Я знаю, что существует много сообщений здесь, но я не мог найти ничего, что соответствует.

У меня есть a System.Timers.Timer это протекало каждые 30 секунд, это переходит к дб и проверяет, существуют ли какие-либо новые задания. Если он находит один, он выполняет задание на текущем потоке (таймер открытый новый поток для каждого прошедшего). В то время как задание работает, я должен уведомить основной поток (где таймер) о прогрессе.

Примечания:

  1. У меня нет UI, таким образом, я не могу сделать beginInvoke (или используйте фоновый поток) как я обычно делаю в winforms.
  2. Я думавший реализовать ISynchronizeInvoke на моем основном классе, но это смотрит немного излишества (возможно, я неправ здесь).
  3. У меня есть событие в моем классе задания и основном регистре класса к нему, и я вызываю событие каждый раз, когда мне нужно, но я волнуюсь, что он мог бы вызвать блокирование.
  4. Каждое задание может занять до 20 минут.
  5. У меня может быть до 20 заданий, работающих одновременно.

Мой вопрос:

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

Спасибо за любую справку.

6
задан ЯegDwight 17 March 2010 в 15:22
поделиться

4 ответа

Вы также можете использовать блокировку для реализации поточно-ориентированного класса 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);
        }
    }
}
2
ответ дан 17 December 2019 в 04:45
поделиться

Вы может уведомить основной поток о ходе выполнения с помощью метода обратного вызова. То есть:

// in the main thread
public void ProgressCallback(int jobNumber, int status)
{
    // handle notification
}

Вы можете передать этот метод обратного вызова рабочему потоку, когда вы его вызываете (то есть как делегат), или код рабочего потока может «знать» об этом неявно. В любом случае работает.

Параметры jobNumber и status являются лишь примерами. Возможно, вы захотите использовать другой способ идентификации выполняемых заданий, и вы можете захотеть использовать перечислимый тип для статуса. Однако имейте в виду, что ProgressCallback будет вызываться несколькими потоками одновременно, поэтому, если вы обновляете какие-либо общие структуры данных или записываете информацию журнала, вам придется защищать эти ресурсы с помощью блокировок или других методов синхронизации.

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

1
ответ дан 17 December 2019 в 04:45
поделиться

Если вы не возражаете против .NET 3.0, вы можете использовать диспетчер Dispatcher для распределения запросов между потоками. Он ведет себя аналогично Control.Invoke () в Windows Forms, но не имеет зависимости от Forms. Однако вам нужно будет добавить ссылку на сборку WindowsBase (часть .NET 3.0 и новее и является основой для WPF)

Если вы не можете зависеть от .NET 3.0, то я бы скажем, вы с самого начала выбрали правильное решение: реализуйте интерфейс ISynchronizeInvoke в своем основном классе и передайте его свойству SynchronizingObject таймера. Затем обратный вызов вашего таймера будет вызван в основном потоке, который затем может порождать BackgroundWorkers , который проверяет БД и запускает любые задания в очереди.Задания будут сообщать о ходе выполнения с помощью события ProgressChanged , которое автоматически выполняет маршалинг вызова в основной поток.

Быстрый поиск в Google показал этот пример того, как на самом деле реализовать интерфейс ISynchronizeInvoke.

1
ответ дан 17 December 2019 в 04:45
поделиться

Использовать события. Например, класс 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();
};
1
ответ дан 17 December 2019 в 04:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: